From e8cddd14bd0eef826973590ae1fa5385d4d76522 Mon Sep 17 00:00:00 2001 From: nsjames Date: Tue, 27 Aug 2019 17:41:30 +0300 Subject: [PATCH 01/72] replaced internals with externals --- electron.js | 9 +- preload.js | 6 + src/components/MenuBar.vue | 13 + src/components/ViewBase.vue | 50 +- src/components/login/SetPassword.vue | 3 +- src/components/login/Welcome.vue | 2 +- src/services/electron/SocketService.js | 19 +- src/services/electron/WebViewService.js | 12 + src/services/electron/WindowService.js | 71 +-- src/store/actions.js | 1 - src/util/ElectronHelpers.js | 41 +- src/views/Account.vue | 326 ------------ src/views/App.vue | 266 ---------- src/views/Apps.vue | 464 ---------------- src/views/Assets.vue | 23 - src/views/Contacts.vue | 16 - src/views/Dashboard.vue | 50 -- src/views/Exchange.vue | 383 ------------- src/views/Histories.vue | 290 ---------- src/views/Identities.vue | 678 ------------------------ src/views/Items.vue | 24 - src/views/Locations.vue | 173 ------ src/views/Login.vue | 4 +- src/views/Networks.vue | 210 -------- src/views/Purchase.vue | 274 ---------- src/views/RIDL.vue | 62 --- src/views/Receive.vue | 101 ---- src/views/Scatter.vue | 92 ++++ src/views/Settings.vue | 180 ------- src/views/Transfer.vue | 223 -------- src/views/Wallet.vue | 229 -------- src/vue/Routing.js | 98 +--- 32 files changed, 197 insertions(+), 4196 deletions(-) create mode 100644 preload.js create mode 100644 src/services/electron/WebViewService.js delete mode 100644 src/views/Account.vue delete mode 100644 src/views/App.vue delete mode 100644 src/views/Apps.vue delete mode 100644 src/views/Assets.vue delete mode 100644 src/views/Contacts.vue delete mode 100644 src/views/Dashboard.vue delete mode 100644 src/views/Exchange.vue delete mode 100644 src/views/Histories.vue delete mode 100644 src/views/Identities.vue delete mode 100644 src/views/Items.vue delete mode 100644 src/views/Locations.vue delete mode 100644 src/views/Networks.vue delete mode 100644 src/views/Purchase.vue delete mode 100644 src/views/RIDL.vue delete mode 100644 src/views/Receive.vue create mode 100644 src/views/Scatter.vue delete mode 100644 src/views/Settings.vue delete mode 100644 src/views/Transfer.vue delete mode 100644 src/views/Wallet.vue diff --git a/electron.js b/electron.js index 79ff2ebd..0f601caa 100644 --- a/electron.js +++ b/electron.js @@ -378,6 +378,7 @@ class LowLevelSocketService { res.setHeader('Content-Type', 'application/json'); res.end('scatter'); } + await Promise.all(Object.keys(this.ports).map(async port => { const server = this.ports[port] ? https.createServer(_certs, requestHandler) : http.createServer(requestHandler); this.websockets.push(new WebSocket.Server({ server })); @@ -466,7 +467,7 @@ global.appShared = { LowLevelSocketService:new LowLevelSocketService(), NotificationService, savingData:false, - reloader:() => mainWindow.reload() + reloader:() => mainWindow.reload(), }; @@ -511,3 +512,9 @@ console.error = (...params) => { mainWindow.webContents.send('console', params); logerr(...params); } + + +// FORWARDING FROM INJECTED DOM +global.scatterMessage = (data) => { + mainWindow.webContents.send('scatter', data); +} \ No newline at end of file diff --git a/preload.js b/preload.js new file mode 100644 index 00000000..85b06c17 --- /dev/null +++ b/preload.js @@ -0,0 +1,6 @@ +const { ipcRenderer: ipc, remote } = require('electron'); +let send = remote.getGlobal('scatterMessage'); + +ipc.on('scatter', (event, data) => window.ScatterWallet.received(data)); +ipc.on('sockets', (event, data) => window.ScatterWallet.sockets(data)); +window.ScatterWallet = { send }; \ No newline at end of file diff --git a/src/components/MenuBar.vue b/src/components/MenuBar.vue index 362b8dd5..c152be54 100644 --- a/src/components/MenuBar.vue +++ b/src/components/MenuBar.vue @@ -5,6 +5,13 @@
+ +
+
+
+
+
+
@@ -45,6 +52,12 @@ }, 500); }, methods:{ + test(){ + this.$router.push({name:this.RouteNames.LOGIN}); + setTimeout(() => { + this.$router.push({name:this.RouteNames.SCATTER}); + },10) + }, quit(){ SocketService.broadcastEvent('dced', {}); setTimeout(() => remote.app.quit(), 1); diff --git a/src/components/ViewBase.vue b/src/components/ViewBase.vue index 105843ba..71416935 100644 --- a/src/components/ViewBase.vue +++ b/src/components/ViewBase.vue @@ -1,6 +1,5 @@ @@ -30,20 +21,13 @@ import { mapActions, mapGetters, mapState } from 'vuex' import {RouteNames, Routing} from '../vue/Routing' - import Processes from './Processes'; - import Popups from './Popups'; - import Sidebar from './Sidebar'; - import QuickActions from './QuickActions'; import MenuBar from './MenuBar'; + import Popups from './Popups'; - import SingletonService from "../services/utility/SingletonService"; export default { components:{ Popups, - Processes, - Sidebar, - QuickActions, MenuBar }, data(){ return { @@ -59,29 +43,9 @@ ...mapGetters([ 'unlocked', ]), - onboarded(){ - return this.unlocked && this.scatter.onboarded && this.route !== RouteNames.LOGIN - }, isPopout(){ return this.$route.name === 'popout'; }, - route(){ - return this.$route.name - }, - showQuickActions(){ - if(!this.onboarded) return false; - return ![ - RouteNames.ITEMS, - RouteNames.NETWORKS, - RouteNames.CONTACTS, - RouteNames.HISTORIES, - RouteNames.RIDL, - RouteNames.SETTINGS, - RouteNames.PURCHASE, - RouteNames.IDENTITIES, - RouteNames.LOCATIONS, - ].includes(this.$route.name); - }, }, mounted(){ @@ -90,16 +54,6 @@ methods:{ }, - watch:{ - ['unlocked'](){ - if(this.$route.fullPath.indexOf('popout') > -1) return; - if(this.initialized) return; - if(this.unlocked){ - this.initialized = true; - SingletonService.init(); - } - } - } } diff --git a/src/components/login/SetPassword.vue b/src/components/login/SetPassword.vue index d1ea5dec..bb180a53 100644 --- a/src/components/login/SetPassword.vue +++ b/src/components/login/SetPassword.vue @@ -47,9 +47,8 @@ if(err) return PopupService.push(Popup.snackbar(err)); if(this.password !== this.confirmation) return PopupService.push(Popup.snackbar("Password confirmation does not match password")); - StoreService.setWorking(true); + // TODO: Loaders await this[UIActions.CREATE_SCATTER](this.password); - StoreService.setWorking(false); this.$emit('next'); }, diff --git a/src/components/login/Welcome.vue b/src/components/login/Welcome.vue index 70e2ae4f..cf7ea696 100644 --- a/src/components/login/Welcome.vue +++ b/src/components/login/Welcome.vue @@ -32,7 +32,7 @@ const scatter = this.scatter.clone(); scatter.onboarded = true; await this[Actions.SET_SCATTER](scatter); - this.$router.push({name:RouteNames.HOME}) + this.$router.push({name:RouteNames.SCATTER}) }, ...mapActions([ Actions.SET_SCATTER diff --git a/src/services/electron/SocketService.js b/src/services/electron/SocketService.js index 1930ba77..c67c451a 100644 --- a/src/services/electron/SocketService.js +++ b/src/services/electron/SocketService.js @@ -3,22 +3,22 @@ import {ipcRenderer, remote} from "../../util/ElectronHelpers"; import StoreService from "@walletpack/core/services/utility/StoreService"; import * as CoreSocketService from '@walletpack/core/services/utility/SocketService'; import * as UIActions from "../../store/ui_actions"; +import WebViewService from "./WebViewService"; - -let websockets = []; export default class SocketService { static async initialize(){ + console.log('initializing') remote.getGlobal('appShared').QuitWatcher = () => { console.log('dced') SocketService.broadcastEvent('dced', {}); }; const certs = await CoreSocketService.getCerts(); - ipcRenderer.on('api', (event, {request, id}) => CoreSocketService.handleApiResponse(request, id)); - ipcRenderer.on('pair', (event, {request, id}) => CoreSocketService.handlePairedResponse(request, id)); - ipcRenderer.on('ports', (event, ports) => StoreService.get().dispatch(UIActions.SET_PORTS, ports)); + ipcRenderer.on('api', (event, {request, id}) => WebViewService.get().send('sockets', {type:'api', request, id})); + ipcRenderer.on('pair', (event, {request, id}) => WebViewService.get().send('sockets', {type:'pair', request, id})); + ipcRenderer.on('ports', (event, ports) => WebViewService.get().send('sockets', {type:'ports', ports})); return remote.getGlobal('appShared').LowLevelSocketService.initialize(certs); } @@ -26,19 +26,20 @@ export default class SocketService { return remote.getGlobal('appShared').LowLevelSocketService.close(); } - static sendEvent(event, payload, origin){ + static async sendEvent(event, payload, origin){ return remote.getGlobal('appShared').LowLevelSocketService.sendEvent(event, payload, origin); } - static broadcastEvent(event, payload){ + static async broadcastEvent(event, payload){ return remote.getGlobal('appShared').LowLevelSocketService.broadcastEvent(event, payload); } - static emit(origin, id, path, data){ + static async emit(origin, id, path, data){ + console.log('emit', origin, path, data); return remote.getGlobal('appShared').LowLevelSocketService.emit(origin, id, path, data); } - static getNewKey(origin, id){ + static async getNewKey(origin, id){ return remote.getGlobal('appShared').LowLevelSocketService.getNewKey(origin, id); } diff --git a/src/services/electron/WebViewService.js b/src/services/electron/WebViewService.js new file mode 100644 index 00000000..0504b3ad --- /dev/null +++ b/src/services/electron/WebViewService.js @@ -0,0 +1,12 @@ +let webView; +export default class WebViewService { + + static set(_wv){ + webView = _wv; + } + + static get(){ + return webView; + } + +} \ No newline at end of file diff --git a/src/services/electron/WindowService.js b/src/services/electron/WindowService.js index 04ca188e..65cd6c05 100644 --- a/src/services/electron/WindowService.js +++ b/src/services/electron/WindowService.js @@ -1,3 +1,5 @@ +import {Popup, PopupData, PopupDisplayTypes} from "../../models/popups/Popup"; + console.log(require('../../util/ElectronHelpers')); import {ipcRenderer, remote} from '../../util/ElectronHelpers'; @@ -72,38 +74,43 @@ export default class WindowService { } static openPopOut(popup){ + return new Promise((resolve) => { + popup = Popup.fromJson(popup); + + let responded = false; + + const scatter = StoreService.get().state.scatter.clone(); + scatter.keychain.keypairs.map(keypair => delete keypair.privateKey); + scatter.keychain.identities.map(identity => delete identity.privateKey); + delete scatter.keychain.avatars; + scatter.recurring = Recurring.placeholder(); + scatter.contacts = []; + + const respond = result => { + popouts = popouts.filter(x => x.id !== popup.id); + // popup.data.callback(Object.assign(popup, {result})); + resolve(Object.assign(popup, {result})); + }; + + // Rate limiting: One open pop out at a time per origin. + if(popouts.find(x => x.data.props.payload.origin === popup.data.props.payload.origin)) + return resolve(false); + + popup.data.props.appData = AppsService.getAppData(popup.data.props.payload.origin); + + popouts.push(popup); + + const {width, height} = popup.dimensions(); + return LowLevelWindowService.openPopOut( + readyWindow => WindowService.sendAndWait(readyWindow.id, 'popup', {scatter, popup, balances:{}}).then(result => { + responded = true; + respond(result); + }), + closedWithoutAction => { if(!responded) respond(null); }, + width, height, + popup.internal + ); + }) - let responded = false; - - const scatter = StoreService.get().state.scatter.clone(); - scatter.keychain.keypairs.map(keypair => delete keypair.privateKey); - scatter.keychain.identities.map(identity => delete identity.privateKey); - delete scatter.keychain.avatars; - scatter.recurring = Recurring.placeholder(); - scatter.contacts = []; - - const respond = result => { - popouts = popouts.filter(x => x.id !== popup.id); - popup.data.callback(Object.assign(popup, {result})); - }; - - // Rate limiting: One open pop out at a time per origin. - if(popouts.find(x => x.data.props.payload.origin === popup.data.props.payload.origin)) - return false; - - popup.data.props.appData = AppsService.getAppData(popup.data.props.payload.origin); - - popouts.push(popup); - - const {width, height} = popup.dimensions(); - return LowLevelWindowService.openPopOut( - readyWindow => WindowService.sendAndWait(readyWindow.id, 'popup', {scatter, popup, balances:{}}).then(result => { - responded = true; - respond(result); - }), - closedWithoutAction => { if(!responded) respond(null); }, - width, height, - popup.internal - ); } } \ No newline at end of file diff --git a/src/store/actions.js b/src/store/actions.js index 6afbcfef..2b24f9bd 100644 --- a/src/store/actions.js +++ b/src/store/actions.js @@ -45,7 +45,6 @@ export const actions = { return commit(Actions.SET_SCATTER, scatter); } - console.log(await PasswordHelpers.verifyPassword(null, forceLocal)); if(await PasswordHelpers.verifyPassword(null, forceLocal)){ const scatter = state.scatter.clone(); diff --git a/src/util/ElectronHelpers.js b/src/util/ElectronHelpers.js index df3a362e..7ac11ea8 100644 --- a/src/util/ElectronHelpers.js +++ b/src/util/ElectronHelpers.js @@ -78,6 +78,7 @@ export default class ElectronHelpers { const eventListener = async (type, data) => { if(type === 'popout') { + console.log('POPOUT!') const popup = new Popup(PopupDisplayTypes.POP_OUT, new PopupData(data.type, data)); return WindowService.openPopOut(popup); } @@ -87,12 +88,7 @@ export default class ElectronHelpers { ScatterCore.initialize( { - blockchains:{ - EOSIO:'eos', - ETH:'eth', - // TRX:'trx', - BTC:'btc', - }, + blockchains:{}, plugins:[ require('@walletpack/eosio').default, require('@walletpack/ethereum').default, @@ -121,34 +117,11 @@ export default class ElectronHelpers { } ); - ExternalWallet.loadWallets([ - {name:'LEDGER', wallet:LedgerWallet} - ]) - - - // ScatterCore.initialize( - // [ - // require('scatter-core/plugins/defaults/eos').default, - // require('scatter-core/plugins/defaults/trx').default, - // require('scatter-core/plugins/defaults/eth').default, - // require('scatter-core/plugins/defaults/btc').default, - // ], - // store, - // StorageService, - // { - // get:() => ipcAsync('seed'), - // set:(seed) => ipcFaF('seeding', seed), - // clear:() => ipcFaF('key', null), - // }, - // { - // getVersion:ElectronHelpers.getVersion, - // pushNotification:ElectronHelpers.pushNotificationMethod(), - // }, - // require("../services/electron/WindowService").default.openPopOut, - // // TODO: - // ElectronHelpers.getLedgerTransport(), - // require("../services/electron/SocketService").default - // ) + // TODO: Ledger + // ExternalWallet.loadWallets([ + // {name:'LEDGER', wallet:LedgerWallet} + // ]) + } static getLedgerTransport(){ diff --git a/src/views/Account.vue b/src/views/Account.vue deleted file mode 100644 index 3152c69c..00000000 --- a/src/views/Account.vue +++ /dev/null @@ -1,326 +0,0 @@ - - - - - diff --git a/src/views/App.vue b/src/views/App.vue deleted file mode 100644 index 9d20e1b1..00000000 --- a/src/views/App.vue +++ /dev/null @@ -1,266 +0,0 @@ - - - - - diff --git a/src/views/Apps.vue b/src/views/Apps.vue deleted file mode 100644 index 693314a2..00000000 --- a/src/views/Apps.vue +++ /dev/null @@ -1,464 +0,0 @@ - - - - - diff --git a/src/views/Assets.vue b/src/views/Assets.vue deleted file mode 100644 index 3d6f8239..00000000 --- a/src/views/Assets.vue +++ /dev/null @@ -1,23 +0,0 @@ - - - - - \ No newline at end of file diff --git a/src/views/Contacts.vue b/src/views/Contacts.vue deleted file mode 100644 index 10da1904..00000000 --- a/src/views/Contacts.vue +++ /dev/null @@ -1,16 +0,0 @@ - - - - - \ No newline at end of file diff --git a/src/views/Dashboard.vue b/src/views/Dashboard.vue deleted file mode 100644 index 55ee6c60..00000000 --- a/src/views/Dashboard.vue +++ /dev/null @@ -1,50 +0,0 @@ - - - - - diff --git a/src/views/Exchange.vue b/src/views/Exchange.vue deleted file mode 100644 index 322736cf..00000000 --- a/src/views/Exchange.vue +++ /dev/null @@ -1,383 +0,0 @@ - - - - - \ No newline at end of file diff --git a/src/views/Histories.vue b/src/views/Histories.vue deleted file mode 100644 index 7c306136..00000000 --- a/src/views/Histories.vue +++ /dev/null @@ -1,290 +0,0 @@ - - - - - \ No newline at end of file diff --git a/src/views/Identities.vue b/src/views/Identities.vue deleted file mode 100644 index d2e9bd77..00000000 --- a/src/views/Identities.vue +++ /dev/null @@ -1,678 +0,0 @@ - - - - - \ No newline at end of file diff --git a/src/views/Items.vue b/src/views/Items.vue deleted file mode 100644 index d996dde7..00000000 --- a/src/views/Items.vue +++ /dev/null @@ -1,24 +0,0 @@ - - - - - diff --git a/src/views/Locations.vue b/src/views/Locations.vue deleted file mode 100644 index dbe7fd88..00000000 --- a/src/views/Locations.vue +++ /dev/null @@ -1,173 +0,0 @@ - - - - - \ No newline at end of file diff --git a/src/views/Login.vue b/src/views/Login.vue index f7ef51f2..5a25074c 100644 --- a/src/views/Login.vue +++ b/src/views/Login.vue @@ -241,11 +241,11 @@ } this.success = true; - this.$router.push({name:this.RouteNames.HOME}); + this.$router.push({name:this.RouteNames.SCATTER}); })) } else { this.success = true; - this.$router.push({name:this.RouteNames.HOME}); + this.$router.push({name:this.RouteNames.SCATTER}); } }, 1000); } else { diff --git a/src/views/Networks.vue b/src/views/Networks.vue deleted file mode 100644 index b2241b28..00000000 --- a/src/views/Networks.vue +++ /dev/null @@ -1,210 +0,0 @@ - - - - - \ No newline at end of file diff --git a/src/views/Purchase.vue b/src/views/Purchase.vue deleted file mode 100644 index 351ce128..00000000 --- a/src/views/Purchase.vue +++ /dev/null @@ -1,274 +0,0 @@ - - - - - diff --git a/src/views/RIDL.vue b/src/views/RIDL.vue deleted file mode 100644 index 1acdd5c4..00000000 --- a/src/views/RIDL.vue +++ /dev/null @@ -1,62 +0,0 @@ - - - - - diff --git a/src/views/Receive.vue b/src/views/Receive.vue deleted file mode 100644 index 994972d4..00000000 --- a/src/views/Receive.vue +++ /dev/null @@ -1,101 +0,0 @@ - - - - - \ No newline at end of file diff --git a/src/views/Scatter.vue b/src/views/Scatter.vue new file mode 100644 index 00000000..a5b98fe6 --- /dev/null +++ b/src/views/Scatter.vue @@ -0,0 +1,92 @@ + + + + + diff --git a/src/views/Settings.vue b/src/views/Settings.vue deleted file mode 100644 index 5690b8fb..00000000 --- a/src/views/Settings.vue +++ /dev/null @@ -1,180 +0,0 @@ - - - - - diff --git a/src/views/Transfer.vue b/src/views/Transfer.vue deleted file mode 100644 index 3b21fa5f..00000000 --- a/src/views/Transfer.vue +++ /dev/null @@ -1,223 +0,0 @@ - - - - - \ No newline at end of file diff --git a/src/views/Wallet.vue b/src/views/Wallet.vue deleted file mode 100644 index 44b0d05a..00000000 --- a/src/views/Wallet.vue +++ /dev/null @@ -1,229 +0,0 @@ - - - - - diff --git a/src/vue/Routing.js b/src/vue/Routing.js index 1a48fcf1..56fc14d8 100644 --- a/src/vue/Routing.js +++ b/src/vue/Routing.js @@ -1,103 +1,21 @@ -// TODO: These should be uncommented for releases import PopOut from '../views/PopOut'; +import Scatter from '../views/Scatter'; import Login from '../views/Login'; -import Apps from '../views/Apps'; -import App from '../views/App'; -import Dashboard from '../views/Dashboard'; -import Assets from '../views/Assets'; -import Wallet from '../views/Wallet'; -import Account from '../views/Account'; -import Items from '../views/Items'; -import Transfer from '../views/Transfer'; -import Exchange from '../views/Exchange'; -import Receive from '../views/Receive'; -import Networks from '../views/Networks'; -import Contacts from '../views/Contacts'; -import Identities from '../views/Identities'; -import Locations from '../views/Locations'; -import Histories from '../views/Histories'; -import Settings from '../views/Settings'; -import RIDL from '../views/RIDL'; -import Purchase from '../views/Purchase'; - -// TODO: These should be commented out for testing. -// const PopOut = () => import('../views/PopOut'); -// const Login = () => import('../views/Login'); -// const Apps = () => import('../views/Apps'); -// const App = () => import('../views/App'); -// const Assets = () => import('../views/Assets'); -// const Wallet = () => import('../views/Wallet'); -// const Account = () => import('../views/Account'); -// const Items = () => import('../views/Items'); -// const Transfer = () => import('../views/Transfer'); -// const Exchange = () => import('../views/Exchange'); -// const Receive = () => import('../views/Receive'); -// const Networks = () => import('../views/Networks'); -// const Contacts = () => import('../views/Contacts'); -// const Identities = () => import('../views/Identities'); -// const Locations = () => import('../views/Locations'); -// const Histories = () => import('../views/Histories'); -// const Settings = () => import('../views/Settings'); -// const RIDL = () => import('../views/RIDL'); -// const Purchase = () => import('../views/Purchase'); - - export const RouteNames = { POP_OUT:'popout', - + SCATTER:'scatter', LOGIN:'login', - APPS:'apps', - HOME:'home', - WALLET:'wallet', - ITEMS:'items', - NETWORKS:'networks', - ASSETS:'assets', - TRANSFER:'transfer', - RECEIVE:'receive', - EXCHANGE:'exchange', - CONTACTS:'contacts', - IDENTITIES:'identities', - LOCATIONS:'locations', - HISTORIES:'histories', - RIDL:'ridl', - SETTINGS:'settings', - PURCHASE:'purchase', - - - APP:'app', - ACCOUNT:'account', }; const RouteViews = { [RouteNames.LOGIN]:Login, - [RouteNames.APPS]:Apps, - [RouteNames.HOME]:Dashboard, - [RouteNames.WALLET]:Wallet, - [RouteNames.ITEMS]:Items, - [RouteNames.NETWORKS]:Networks, - [RouteNames.ASSETS]:Assets, - [RouteNames.TRANSFER]:Transfer, - [RouteNames.RECEIVE]:Receive, - [RouteNames.EXCHANGE]:Exchange, - [RouteNames.CONTACTS]:Contacts, - [RouteNames.IDENTITIES]:Identities, - [RouteNames.LOCATIONS]:Locations, - [RouteNames.HISTORIES]:Histories, - [RouteNames.RIDL]:RIDL, - [RouteNames.SETTINGS]:Settings, - [RouteNames.PURCHASE]:Purchase, - - - [RouteNames.APP]:App, - [RouteNames.ACCOUNT]:Account, + [RouteNames.SCATTER]:Scatter, [RouteNames.POP_OUT]:PopOut, }; const RoutePaths = { - [RouteNames.HOME]: '/', - [RouteNames.APP]: '/app/:applink', - [RouteNames.ACCOUNT]: '/account/:unique', + [RouteNames.SCATTER]: '/', }; export class Routing { @@ -126,14 +44,6 @@ export class Routing { return ![ RouteNames.LOGIN, RouteNames.POP_OUT, - // RouteNames.LOAD_FROM_BACKUP - ].includes(routeName) - } - - static hasSidebar(routeName){ - return ![ - RouteNames.ONBOARDING, - RouteNames.POP_OUT, ].includes(routeName) } From d2c76b58fa001f321c48f3b0821835187e4545a4 Mon Sep 17 00:00:00 2001 From: nsjames Date: Sat, 31 Aug 2019 01:14:54 +0300 Subject: [PATCH 02/72] revamp of communications --- electron.js | 521 +------------- electron/app.js | 232 ++++++ electron/security.js | 20 + electron/services/files.js | 24 + electron/services/notifier.js | 17 + electron/services/sockets.js | 162 +++++ electron/services/storage.js | 96 +++ electron/services/wallet.js | 113 +++ electron/services/windows.js | 89 +++ electron/utils.js | 27 + package.json | 12 +- preload.js | 18 +- src/components/Popups.vue | 116 +-- src/components/Processes.vue | 119 ---- src/components/QuickActions.vue | 263 ------- src/components/Sidebar.vue | 267 ------- src/components/TokenBar.vue | 124 ---- src/components/ViewBase.vue | 19 +- src/components/login/SetPassword.vue | 1 - src/components/misc/Assets.vue | 198 ------ src/components/misc/Card.vue | 153 ---- src/components/misc/Contacts.vue | 167 ----- src/components/misc/CreditCardsList.vue | 54 -- src/components/misc/EditContact.vue | 91 --- src/components/misc/EditNetwork.vue | 182 ----- src/components/misc/GetRIDL.vue | 271 ------- src/components/misc/KeysAndAccountList.vue | 495 ------------- .../panels/keypair/ImportHardwareKey.vue | 198 ------ .../panels/keypair/ImportPrivateKey.vue | 53 -- src/components/panels/keypair/ImportQRKey.vue | 67 -- .../panels/settings/SettingsBackup.vue | 76 -- .../panels/settings/SettingsDestroy.vue | 50 -- .../panels/settings/SettingsExplorer.vue | 78 -- .../panels/settings/SettingsFirewall.vue | 165 ----- .../panels/settings/SettingsGeneral.vue | 180 ----- .../panels/settings/SettingsPassword.vue | 123 ---- .../panels/settings/SettingsTokens.vue | 325 --------- .../popins/fullscreen/AddCustomNetwork.vue | 63 -- .../popins/fullscreen/AddNewContact.vue | 70 -- .../popins/fullscreen/ChangeIdentityKey.vue | 109 --- .../popins/fullscreen/CheckHardware.vue | 63 -- .../popins/fullscreen/ConfirmPassword.vue | 80 --- .../popins/fullscreen/DestroyScatter.vue | 6 +- .../popins/fullscreen/EnterSecurityCode.vue | 78 -- .../fullscreen/EosChangePermissions.vue | 131 ---- .../popins/fullscreen/EosCreateAccount.vue | 430 ----------- .../popins/fullscreen/EosModerateCpuNet.vue | 236 ------- .../popins/fullscreen/EosModerateRam.vue | 237 ------- .../popins/fullscreen/EosProxyVotes.vue | 220 ------ .../popins/fullscreen/ExportPrivateKey.vue | 287 -------- .../popins/fullscreen/GenerateKeypair.vue | 98 --- .../popins/fullscreen/ImportBackup.vue | 7 +- .../popins/fullscreen/ImportKeypair.vue | 238 ------- .../popins/fullscreen/RemoveKeypair.vue | 84 --- .../popins/fullscreen/UnlinkAccount.vue | 98 --- .../popins/overlay/ConfirmExchange.vue | 63 -- .../popins/overlay/ConfirmTransfer.vue | 59 -- src/components/popins/overlay/EnterPIN.vue | 63 -- .../popins/overlay/EosLinkAccount.vue | 125 ---- src/components/popins/overlay/Prompt.vue | 84 --- .../popins/overlay/SelectAccount.vue | 54 -- .../popins/overlay/SelectBlockchain.vue | 55 -- .../popins/overlay/SelectDisplayToken.vue | 87 --- .../popins/overlay/SelectFromList.vue | 92 --- .../popins/overlay/SelectKeypair.vue | 52 -- .../popins/overlay/SelectRecipient.vue | 99 --- src/components/popins/overlay/SelectToken.vue | 98 --- .../popins/overlay/SelectTokenAndAccount.vue | 54 -- .../popins/overlay/TransactionSuccess.vue | 84 --- .../popins/overlay/UpdateAvailable.vue | 100 --- src/components/popouts/PopOutApp.vue | 127 ---- src/components/popouts/PopOutHead.vue | 123 ---- src/components/popouts/RequiredFields.vue | 68 -- src/components/svgs/CreditCard.vue | 4 - .../svgs/quick-actions/Exchange.vue | 3 - src/components/svgs/quick-actions/Receive.vue | 3 - src/components/svgs/quick-actions/Refresh.vue | 60 -- src/components/svgs/quick-actions/Send.vue | 3 - src/components/tokens/TokenGraph.vue | 188 ----- src/components/tokens/TokenList.vue | 611 ---------------- src/data/Countries.js | 246 ------- src/main.js | 45 +- src/migrations/10.0.0.js | 23 - src/migrations/10.1.0.js | 24 - src/migrations/10.2.0.js | 29 - src/migrations/11.0.0.js | 59 -- src/migrations/9.0.0.js | 44 -- src/migrations/9.2.0.js | 19 - src/migrations/9.5.0.js | 12 - src/migrations/version.js | 7 - src/models/hardware/LedgerWallet.js | 7 +- src/services/electron/SocketService.js | 3 - src/services/electron/StorageService.js | 5 +- src/services/electron/WebViewService.js | 1 + src/services/electron/WindowService.js | 95 +-- src/services/utility/BackupService.js | 18 +- src/services/utility/LanguageService.js | 53 -- src/services/utility/PasswordHelpers.js | 129 ---- src/services/utility/PopupService.js | 10 +- src/services/utility/RIDLService.js | 413 ----------- src/services/utility/RecurringService.js | 70 -- src/services/utility/SingletonService.js | 26 - src/services/utility/UpdateService.js | 46 -- src/store/actions.js | 90 +-- src/store/mutations.js | 33 - src/store/store.js | 63 +- src/store/ui_actions.js | 16 - src/util/ElectronHelpers.js | 12 +- src/util/WalletTalk.js | 53 ++ src/views/Login.vue | 105 +-- src/views/PopOut.vue | 217 ++---- src/views/Scatter.vue | 92 +-- src/views/popouts/AppLogin.vue | 458 ------------ src/views/popouts/GetPublicKey.vue | 92 --- src/views/popouts/LinkApp.vue | 84 --- src/views/popouts/Signature.vue | 666 ------------------ src/views/popouts/TransferRequest.vue | 279 -------- src/views/popouts/UpdateIdentity.vue | 110 --- src/vue/VueInitializer.js | 121 +--- yarn.lock | 48 +- 120 files changed, 1145 insertions(+), 12838 deletions(-) create mode 100644 electron/app.js create mode 100644 electron/security.js create mode 100644 electron/services/files.js create mode 100644 electron/services/notifier.js create mode 100644 electron/services/sockets.js create mode 100644 electron/services/storage.js create mode 100644 electron/services/wallet.js create mode 100644 electron/services/windows.js create mode 100644 electron/utils.js delete mode 100644 src/components/Processes.vue delete mode 100644 src/components/QuickActions.vue delete mode 100644 src/components/Sidebar.vue delete mode 100644 src/components/TokenBar.vue delete mode 100644 src/components/misc/Assets.vue delete mode 100644 src/components/misc/Card.vue delete mode 100644 src/components/misc/Contacts.vue delete mode 100644 src/components/misc/CreditCardsList.vue delete mode 100644 src/components/misc/EditContact.vue delete mode 100644 src/components/misc/EditNetwork.vue delete mode 100644 src/components/misc/GetRIDL.vue delete mode 100644 src/components/misc/KeysAndAccountList.vue delete mode 100644 src/components/panels/keypair/ImportHardwareKey.vue delete mode 100644 src/components/panels/keypair/ImportPrivateKey.vue delete mode 100644 src/components/panels/keypair/ImportQRKey.vue delete mode 100644 src/components/panels/settings/SettingsBackup.vue delete mode 100644 src/components/panels/settings/SettingsDestroy.vue delete mode 100644 src/components/panels/settings/SettingsExplorer.vue delete mode 100644 src/components/panels/settings/SettingsFirewall.vue delete mode 100644 src/components/panels/settings/SettingsGeneral.vue delete mode 100644 src/components/panels/settings/SettingsPassword.vue delete mode 100644 src/components/panels/settings/SettingsTokens.vue delete mode 100644 src/components/popins/fullscreen/AddCustomNetwork.vue delete mode 100644 src/components/popins/fullscreen/AddNewContact.vue delete mode 100644 src/components/popins/fullscreen/ChangeIdentityKey.vue delete mode 100644 src/components/popins/fullscreen/CheckHardware.vue delete mode 100644 src/components/popins/fullscreen/ConfirmPassword.vue delete mode 100644 src/components/popins/fullscreen/EnterSecurityCode.vue delete mode 100644 src/components/popins/fullscreen/EosChangePermissions.vue delete mode 100644 src/components/popins/fullscreen/EosCreateAccount.vue delete mode 100644 src/components/popins/fullscreen/EosModerateCpuNet.vue delete mode 100644 src/components/popins/fullscreen/EosModerateRam.vue delete mode 100644 src/components/popins/fullscreen/EosProxyVotes.vue delete mode 100644 src/components/popins/fullscreen/ExportPrivateKey.vue delete mode 100644 src/components/popins/fullscreen/GenerateKeypair.vue delete mode 100644 src/components/popins/fullscreen/ImportKeypair.vue delete mode 100644 src/components/popins/fullscreen/RemoveKeypair.vue delete mode 100644 src/components/popins/fullscreen/UnlinkAccount.vue delete mode 100644 src/components/popins/overlay/ConfirmExchange.vue delete mode 100644 src/components/popins/overlay/ConfirmTransfer.vue delete mode 100644 src/components/popins/overlay/EnterPIN.vue delete mode 100644 src/components/popins/overlay/EosLinkAccount.vue delete mode 100644 src/components/popins/overlay/Prompt.vue delete mode 100644 src/components/popins/overlay/SelectAccount.vue delete mode 100644 src/components/popins/overlay/SelectBlockchain.vue delete mode 100644 src/components/popins/overlay/SelectDisplayToken.vue delete mode 100644 src/components/popins/overlay/SelectFromList.vue delete mode 100644 src/components/popins/overlay/SelectKeypair.vue delete mode 100644 src/components/popins/overlay/SelectRecipient.vue delete mode 100644 src/components/popins/overlay/SelectToken.vue delete mode 100644 src/components/popins/overlay/SelectTokenAndAccount.vue delete mode 100644 src/components/popins/overlay/TransactionSuccess.vue delete mode 100644 src/components/popins/overlay/UpdateAvailable.vue delete mode 100644 src/components/popouts/PopOutApp.vue delete mode 100644 src/components/popouts/PopOutHead.vue delete mode 100644 src/components/popouts/RequiredFields.vue delete mode 100644 src/components/svgs/CreditCard.vue delete mode 100644 src/components/svgs/quick-actions/Exchange.vue delete mode 100644 src/components/svgs/quick-actions/Receive.vue delete mode 100644 src/components/svgs/quick-actions/Refresh.vue delete mode 100644 src/components/svgs/quick-actions/Send.vue delete mode 100644 src/components/tokens/TokenGraph.vue delete mode 100644 src/components/tokens/TokenList.vue delete mode 100644 src/data/Countries.js delete mode 100644 src/migrations/10.0.0.js delete mode 100644 src/migrations/10.1.0.js delete mode 100644 src/migrations/10.2.0.js delete mode 100644 src/migrations/11.0.0.js delete mode 100644 src/migrations/9.0.0.js delete mode 100644 src/migrations/9.2.0.js delete mode 100644 src/migrations/9.5.0.js delete mode 100644 src/migrations/version.js delete mode 100644 src/services/utility/LanguageService.js delete mode 100644 src/services/utility/PasswordHelpers.js delete mode 100644 src/services/utility/RIDLService.js delete mode 100644 src/services/utility/RecurringService.js delete mode 100644 src/services/utility/SingletonService.js delete mode 100644 src/services/utility/UpdateService.js create mode 100644 src/util/WalletTalk.js delete mode 100644 src/views/popouts/AppLogin.vue delete mode 100644 src/views/popouts/GetPublicKey.vue delete mode 100644 src/views/popouts/LinkApp.vue delete mode 100644 src/views/popouts/Signature.vue delete mode 100644 src/views/popouts/TransferRequest.vue delete mode 100644 src/views/popouts/UpdateIdentity.vue diff --git a/electron.js b/electron.js index 0f601caa..0fc54d9e 100644 --- a/electron.js +++ b/electron.js @@ -1,520 +1 @@ -const electron = require('electron'); -const {remote, app, BrowserWindow, Tray, Menu, MenuItem, ipcMain} = electron; -const path = require("path"); -const url = require("url"); - - -const isDev = process.mainModule.filename.indexOf('app.asar') === -1; - -let icon = isDev - ? 'static/icons/icon.png' - : __dirname + '/static/icons/icon.png'; - -let trayIcon = isDev - ? 'static/icons/icon-tray.png' - : __dirname + '/static/icons/icon-tray.png'; - -let mainUrl = isPopup => isDev ? `http://localhost:8080/${isPopup ? '/#/popout' : ''}` : url.format({ - pathname: path.join(__dirname, "dist", "index.html"), - protocol: "file:", - slashes: true, - hash:isPopup ? '/popout' : null -}); - - -const quit = () => { - if(global && global.appShared && global.appShared.savingData){ - setTimeout(() => { - quit(); - }, 100); - } else { - if(global && global.appShared && global.appShared.QuitWatcher !== null) - global.appShared.QuitWatcher(); - app.quit(); - } -} - -let tray, mainWindow; - -const setupMenu = () => { - const menu = new Menu(); - mainWindow.setMenu(menu); - - const template = [{ - label: "Application", - submenu: [ - { label: "About Application", selector: "orderFrontStandardAboutPanel:" }, - { type: "separator" }, - { label: "Quit", accelerator: "Command+Q", click: () => { quit(); }} - ]}, { - label: "Edit", - submenu: [ - { label: "Undo", accelerator: "CmdOrCtrl+Z", selector: "undo:" }, - { label: "Redo", accelerator: "Shift+CmdOrCtrl+Z", selector: "redo:" }, - { type: "separator" }, - { label: "Cut", accelerator: "CmdOrCtrl+X", selector: "cut:" }, - { label: "Copy", accelerator: "CmdOrCtrl+C", selector: "copy:" }, - { label: "Paste", accelerator: "CmdOrCtrl+V", selector: "paste:" }, - { label: "Select All", accelerator: "CmdOrCtrl+A", selector: "selectAll:" } - ]} - ]; - - Menu.setApplicationMenu(Menu.buildFromTemplate(template)); -}; - -const restoreInstance = () => { - mainWindow.show(); - if(mainWindow.isMinimized()) mainWindow.restore(); -}; - -const activateInstance = e => { - if(e) e.preventDefault(); - if(!mainWindow) return; - restoreInstance(); -}; - -const setupTray = () => { - tray = new Tray(trayIcon); - const contextMenu = Menu.buildFromTemplate([ - {label: 'Open', type: 'normal', click:() => restoreInstance()}, - {label: 'Exit', type: 'normal', click:() => quit()} - ]); - tray.setToolTip('Scatter Desktop Companion'); - tray.setContextMenu(contextMenu); - - tray.on('click', () => restoreInstance()) -}; - -const createScatterInstance = () => { - app.setAsDefaultProtocolClient('scatter'); - - const createMainWindow = (show, backgroundColor) => new BrowserWindow({ - width: 1024, - height: 800, - frame: false, - radii: [5,5,5,5], - icon, - resizable: true, - minWidth: 800, - minHeight:720, - titleBarStyle:'hiddenInset', - backgroundColor, - show, - webPreferences:{ - nodeIntegration:true, - webviewTag:true, - } - }); - - mainWindow = createMainWindow(false, '#fff'); - mainWindow.loadURL(mainUrl(false)); - - mainWindow.once('ready-to-show', () => { - mainWindow.show(); - mainWindow.focus(); - }); - - mainWindow.openDevTools(); - mainWindow.on('closed', () => mainWindow = null); - mainWindow.on('close', () => quit()); - - setupTray(); - setupMenu(); - - LowLevelWindowService.queuePopup(); -}; - -app.on('ready', createScatterInstance); -app.on('activate', activateInstance); -app.on('window-all-closed', () => quit()) -app.on('second-instance', (event, argv) => { - if (process.platform === 'win32' || process.platform === 'linux') callDeepLink(argv.slice(1)); - if (mainWindow) activateInstance(); -}) - -app.on('certificate-error', (event, webContents, url, error, certificate, callback) => { - const isLocal = url.startsWith('https://127.0.0.1'); - if (isLocal) { - event.preventDefault() - callback(true) - } else { - callback(false) - } -}) - -app.on('web-contents-created', (event, contents) => { - contents.on('will-navigate', (event, navigationUrl) => { - // Never navigate away from localhost. - if(navigationUrl.indexOf(mainUrl(false)) !== 0) event.preventDefault() - }) -}) - -const callDeepLink = url => { - if(global.appShared.ApiWatcher !== null) - global.appShared.ApiWatcher(url); -} - -if(!app.requestSingleInstanceLock()) quit(); - -app.on('will-finish-launching', () => { - app.on('open-url', (e, url) => { - e.preventDefault(); - callDeepLink(url) - }) -}); - - - - - - - - - - - - - -const isMac = () => process.platform === 'darwin'; -let waitingPopup; -class LowLevelWindowService { - - static getWindow(width = 800, height = 600){ - return new Promise(resolve => { - const win = new BrowserWindow({ - backgroundColor:'#FFFFFF', - width, height, - frame: false, radii: [5,5,5,5], - icon:'assets/icon.png', - show:false, - webPreferences:{ - nodeIntegration:true, - } }); - win.loadURL(mainUrl(true)); - resolve(win) - // win.once('ready-to-show', () => resolve(win)); - }) - } - - static async queuePopup(){ - setTimeout(async () => { - waitingPopup = await this.getWindow(800,600); - }, 100); - } - - static async openPopOut(onReady = () => {}, onClosed = () => {}, width = 800, height = 600, dontHide = false){ - let win = waitingPopup; - if(!win) win = await this.getWindow(); - else waitingPopup = null; - - win.setSize(width, height); - - // Getting the screen to display the popup based on - // where the user is at the time ( for dual monitors ) - const mousePoint = electron.screen.getCursorScreenPoint(); - const activeDisplay = electron.screen.getDisplayNearestPoint(mousePoint); - let {width:screenWidth, height:screenHeight} = activeDisplay.workAreaSize; - const leftBound = activeDisplay.bounds.x; - - let bounds = electron.screen.getPrimaryDisplay().bounds; - let x = bounds.x + (leftBound + ((bounds.width - width) / 2)); - let y = bounds.y + ((bounds.height - height) / 2); - win.setPosition(Math.round(x),Math.round(y)); - - win.once('closed', async () => { - // This is a fix for MacOS systems which causes the - // main window to always pop up after popups closing. - if (!dontHide && isMac()) { - // mainWindow.hide(); - Menu.sendActionToFirstResponder('hide:'); - // mainWindow.show(); - } - - onClosed(win); - win = null; - }); - - onReady(win); - - this.queuePopup(); - - win.show(); - win.setAlwaysOnTop(true, "floating"); - win.focus(); - - if(isMac()){ - app.dock.hide(); - win.setAlwaysOnTop(false); - win.setVisibleOnAllWorkspaces(true); - win.setFullScreenable(false); - app.dock.show(); - } - - return win; - } -} - -const notifier = require("node-notifier"); -class NotificationService { - static pushNotification(title, body){ - notifier.notify({ - message:body, - title, - appID:'com.get-scatter.server', - sound: false, - icon, - wait:false - }); - } -} - -require("babel-polyfill") -const Transport = require('@ledgerhq/hw-transport-node-hid'); - - - - - - - - - - - - - - - -const http = require('http'); -const https = require('https'); -const WebSocket = require('ws'); -const net = require('net'); - - -class LowLevelSocketService { - - constructor(){ - this.rekeyPromise = null; - this.openConnections = {}; - this.websockets = []; - this.ports = {}; - } - - async getNewKey(origin, id){ - return new Promise((resolve, reject) => { - this.rekeyPromise = {resolve, reject}; - this.emit(origin, id, 'rekey'); - return this.rekeyPromise; - }) - } - - async emit(origin, id, path, data){ - const socket = this.openConnections[origin+id]; - return this.emitSocket(socket, path, data); - } - - async emitSocket(socket, path, data){ - if(!socket) return console.error('No socket found'); - socket.send('42/scatter,' + JSON.stringify([path, data ? data : false])) - } - - async initialize(_certs){ - - const socketHandler = socket => { - let origin = null; - - socket.send("40"); - socket.send("40/scatter"); - socket.send(`42/scatter,["connected"]`); - - const id = Math.round(Math.random() * 999999999).toString(); - - // Just logging errors for debugging purposes (dev only) - if(isDev) socket.on('error', async request => console.log('error', request)); - - // Different clients send different message types for disconnect (ws vs socket.io) - socket.on('close', () => delete this.openConnections[origin+id]); - socket.on('disconnect', () => delete this.openConnections[origin+id]); - - socket.on('message', msg => { - if(msg.indexOf('42/scatter') === -1) return false; - const [type, request] = JSON.parse(msg.replace('42/scatter,', '')); - - const killRequest = () => this.emitSocket(socket, 'api', {id:request.id, result:null}); - - if(!request.plugin || request.plugin.length > 100) return killRequest(); - request.plugin = request.plugin.replace(/\s/g, ""); - - if(request.plugin.trim().toLowerCase() === 'Scatter') return killRequest(); - if(request.data.hasOwnProperty('payload') && request.data.payload.origin.trim().toLowerCase() === 'Scatter') return killRequest(); - - let requestOrigin; - if(request.data.hasOwnProperty('payload')) requestOrigin = request.data.payload.origin; - else requestOrigin = request.data.origin; - - if(!origin) origin = requestOrigin; - else if(origin && requestOrigin !== origin) return this.emitSocket(socket, 'api', {id:request.id, result:null}); - if(!this.openConnections.hasOwnProperty(origin+id)) this.openConnections[origin+id] = socket; - - switch(type){ - case 'pair': return mainWindow.webContents.send('pair', {request, id}); - case 'rekeyed': return this.rekeyPromise.resolve(request); - case 'api': return mainWindow.webContents.send('api', {request, id}); - } - - }); - } - - if(this.websockets.length) return this.websockets; - - await this.findOpenPorts(); - mainWindow.webContents.send('ports', this.ports); - - const requestHandler = (_, res) => { - res.setHeader('Access-Control-Allow-Origin', '*'); - res.setHeader('Access-Control-Request-Method', '*'); - res.setHeader('Access-Control-Allow-Methods', 'OPTIONS, GET'); - res.setHeader('Access-Control-Allow-Headers', '*'); - res.setHeader('Content-Type', 'application/json'); - res.end('scatter'); - } - - await Promise.all(Object.keys(this.ports).map(async port => { - const server = this.ports[port] ? https.createServer(_certs, requestHandler) : http.createServer(requestHandler); - this.websockets.push(new WebSocket.Server({ server })); - server.listen(port); - - return true; - })); - - this.websockets.map(ws => ws.on('connection', socketHandler)); - return this.websockets; - } - - async close(){ - this.websockets.map(ws => { - if(typeof ws.clients.map === 'function') ws.clients.map(ws => ws.terminate()); - }) - - return true; - } - - sendEvent(event, payload, origin){ - const sockets = Object.keys(this.openConnections).filter(x => x.indexOf(origin) === 0).map(x => this.openConnections[x]); - sockets.map(x => this.emitSocket(x, 'event', {event, payload})); - return true; - } - - broadcastEvent(event, payload){ - Object.keys(this.openConnections).map(origin => this.sendEvent(event, payload, origin)); - return true; - } - - async findOpenPorts(){ - const isPortAvailable = (port = 0) => { - return new Promise(async resolve => { - const server = net.createServer(); - - server.once('error', err => resolve(err.code !== 'EADDRINUSE')); - - server.once('listening', () => { - server.close(); - resolve(true); - }); - - server.listen(port); - }) - } - - const findPort = async (delta=0) => { - let port = 50005+delta; - while(!await isPortAvailable(port)) port+=1500; - return port; - }; - - const http = await findPort(); - const https = await findPort(1); - this.ports = {[http]:false, [https]:true}; - return true; - } - -} - - - - - - - - - - - - - - - - - - - - -global.appShared = { - Transport, - QuitWatcher:null, - ApiWatcher:null, - LowLevelWindowService, - LowLevelSocketService:new LowLevelSocketService(), - NotificationService, - savingData:false, - reloader:() => mainWindow.reload(), -}; - - - -const secp256k1 = require('secp256k1'); -let seed, key; -ipcMain.on('key', (event, arg) => { - if(event.sender.history[0].indexOf('popout') > -1) return; - if(arg === null) return key = null; - if(key) return; - key = Buffer.from(arg, 'base64'); -}); -ipcMain.on('seeding', (event, arg) => seed = arg); -ipcMain.on('seed', (event, arg) => { - const {data, sig} = arg; - if(!isDev && !secp256k1.verify(Buffer.from(data), Buffer.from(sig, 'base64'), key)) return event.sender.send('seed', null); - event.sender.send('seed', seed); -}); - - - - - -// ------------------------------------------------------------- -// CATCH ALL EXCEPTIONS AND CONSOLES -// We're throwing all exceptions and console up to the renderer console so they can be visible in running builds. -// ------------------------------------------------------------- -process.on('uncaughtException', (err) => { - mainWindow.webContents.send('error', {message:err.message, file:err.fileName, line:err.lineNumber}); - console.error('There was an uncaught error', err) - // process.exit(1) //mandatory (as per the Node docs) -}); - -const log = console.log; -console.log = (...params) => { - mainWindow.webContents.send('console', params); - log(...params); -} - -const logerr = console.error; -console.error = (...params) => { - mainWindow.webContents.send('console', params); - logerr(...params); -} - - -// FORWARDING FROM INJECTED DOM -global.scatterMessage = (data) => { - mainWindow.webContents.send('scatter', data); -} \ No newline at end of file +require('./electron/app') \ No newline at end of file diff --git a/electron/app.js b/electron/app.js new file mode 100644 index 00000000..d8e7621b --- /dev/null +++ b/electron/app.js @@ -0,0 +1,232 @@ +const LowLevelWindowService = require("./services/windows"); +const LowLevelSocketService = require('./services/sockets'); +const NotificationService = require('./services/notifier'); + + +require("babel-polyfill") + +const electron = require('electron'); +const {remote, app, BrowserWindow, Tray, Menu, MenuItem, ipcMain} = electron; +const {isDev, icon, trayIcon, mainUrl} = require('./utils'); + + + + + +const quit = () => { + if(global && global.appShared && global.appShared.savingData){ + setTimeout(() => { + quit(); + }, 100); + } else { + if(global && global.appShared && global.appShared.QuitWatcher !== null) + global.appShared.QuitWatcher(); + app.quit(); + } +} + +let tray, mainWindow; + +const setupMenu = () => { + const menu = new Menu(); + mainWindow.setMenu(menu); + + const template = [{ + label: "Application", + submenu: [ + { label: "About Application", selector: "orderFrontStandardAboutPanel:" }, + { type: "separator" }, + { label: "Quit", accelerator: "Command+Q", click: () => { quit(); }} + ]}, { + label: "Edit", + submenu: [ + { label: "Undo", accelerator: "CmdOrCtrl+Z", selector: "undo:" }, + { label: "Redo", accelerator: "Shift+CmdOrCtrl+Z", selector: "redo:" }, + { type: "separator" }, + { label: "Cut", accelerator: "CmdOrCtrl+X", selector: "cut:" }, + { label: "Copy", accelerator: "CmdOrCtrl+C", selector: "copy:" }, + { label: "Paste", accelerator: "CmdOrCtrl+V", selector: "paste:" }, + { label: "Select All", accelerator: "CmdOrCtrl+A", selector: "selectAll:" } + ]} + ]; + + Menu.setApplicationMenu(Menu.buildFromTemplate(template)); +}; + +const restoreInstance = () => { + mainWindow.show(); + if(mainWindow.isMinimized()) mainWindow.restore(); +}; + +const activateInstance = e => { + if(e) e.preventDefault(); + if(!mainWindow) return; + restoreInstance(); +}; + +const setupTray = () => { + tray = new Tray(trayIcon); + const contextMenu = Menu.buildFromTemplate([ + {label: 'Open', type: 'normal', click:() => restoreInstance()}, + {label: 'Exit', type: 'normal', click:() => quit()} + ]); + tray.setToolTip('Scatter Desktop Companion'); + tray.setContextMenu(contextMenu); + + tray.on('click', () => restoreInstance()) +}; + +const createScatterInstance = () => { + app.setAsDefaultProtocolClient('scatter'); + + const createMainWindow = (show, backgroundColor) => new BrowserWindow({ + width: 1024, + height: 800, + frame: false, + radii: [5,5,5,5], + icon, + resizable: true, + minWidth: 800, + minHeight:720, + titleBarStyle:'hiddenInset', + backgroundColor, + show, + webPreferences:{ + nodeIntegration:true, + webviewTag:true, + } + }); + + mainWindow = createMainWindow(false, '#fff'); + mainWindow.loadURL(mainUrl(false)); + + mainWindow.once('ready-to-show', () => { + mainWindow.show(); + mainWindow.focus(); + }); + + mainWindow.openDevTools(); + mainWindow.on('closed', () => mainWindow = null); + mainWindow.on('close', () => quit()); + + setupTray(); + setupMenu(); + + + LowLevelSocketService.setWindow(mainWindow); + require('./security'); +}; + +app.on('ready', createScatterInstance); +app.on('activate', activateInstance); +app.on('window-all-closed', () => quit()) +app.on('second-instance', (event, argv) => { + if (process.platform === 'win32' || process.platform === 'linux') callDeepLink(argv.slice(1)); + if (mainWindow) activateInstance(); +}) + +app.on('certificate-error', (event, webContents, url, error, certificate, callback) => { + const isLocal = url.startsWith('https://127.0.0.1'); + if (isLocal) { + event.preventDefault() + callback(true) + } else { + callback(false) + } +}) + +app.on('web-contents-created', (event, contents) => { + contents.on('will-navigate', (event, navigationUrl) => { + // Never navigate away from localhost. + if(navigationUrl.indexOf(mainUrl(false)) !== 0) event.preventDefault() + }) +}) + +const callDeepLink = url => { + if(global.appShared.ApiWatcher !== null) + global.appShared.ApiWatcher(url); +} + +if(!app.requestSingleInstanceLock()) quit(); + +app.on('will-finish-launching', () => { + app.on('open-url', (e, url) => { + e.preventDefault(); + callDeepLink(url) + }) +}); + + +const Transport = require('@ledgerhq/hw-transport-node-hid'); + + + +global.appShared = { + Transport, + QuitWatcher:null, + ApiWatcher:null, + LowLevelWindowService, + LowLevelSocketService:new LowLevelSocketService(), + NotificationService, + savingData:false, + reloader:() => mainWindow.reload(), + + Wallet:require('./services/wallet'), + Storage:require('./services/storage'), +}; + + + + + + + + + + +// ------------------------------------------------------------- +// CATCH ALL EXCEPTIONS AND CONSOLES +// We're throwing all exceptions and console up to the renderer console so they can be visible in running builds. +// ------------------------------------------------------------- +process.on('uncaughtException', (err) => { + mainWindow.webContents.send('error', {message:err.message, file:err.fileName, line:err.lineNumber}); + console.error('There was an uncaught error', err) + // process.exit(1) //mandatory (as per the Node docs) +}); + +const log = console.log; +console.log = (...params) => { + mainWindow.webContents.send('console', params); + log(...params); +} + +const logerr = console.error; +console.error = (...params) => { + mainWindow.webContents.send('console', params); + logerr(...params); +} + + +const wallet = require('./services/wallet'); + +// FORWARDING FROM INJECTED DOM +global.scatterMessage = async (data) => { + if(data.service === 'popout' && data.method === 'response') { + mainWindow.webContents.send('popoutResponse', data); + return null; + } + + if(data.method === 'getScatter') return {data:wallet.getScatter(), id:data.id}; + + // Popouts can only get scatter data, not update it + if(data.isPopOut) return; + + if(data.method === 'setScatter') return {data:await wallet.updateScatter(...data.data), id:data.id}; + + mainWindow.webContents.send('scatter', data); +} + +global.scatterLog = (data) => mainWindow.webContents.send('scatterLog', data); + + + diff --git a/electron/security.js b/electron/security.js new file mode 100644 index 00000000..52581661 --- /dev/null +++ b/electron/security.js @@ -0,0 +1,20 @@ +const secp256k1 = require('secp256k1'); +const electron = require('electron'); +const {ipcMain} = electron; +const {isDev} = require('./utils'); + + +let seed, key; + +ipcMain.on('key', (event, arg) => { + if(event.sender.history[0].indexOf('popout') > -1) return; + if(arg === null) return key = null; + if(key) return; + key = Buffer.from(arg, 'base64'); +}); +ipcMain.on('seeding', (event, arg) => seed = arg); +ipcMain.on('seed', (event, arg) => { + const {data, sig} = arg; + if(!isDev && !secp256k1.verify(Buffer.from(data), Buffer.from(sig, 'base64'), key)) return event.sender.send('seed', null); + event.sender.send('seed', seed); +}); \ No newline at end of file diff --git a/electron/services/files.js b/electron/services/files.js new file mode 100644 index 00000000..ce0a3580 --- /dev/null +++ b/electron/services/files.js @@ -0,0 +1,24 @@ +const {remote} = require('electron'); +const fs = require('fs'); + +const getFileLocation = (extensions) => remote.dialog.showOpenDialog({ filters: [ { name: 'only', extensions } ] }); +const getFolderLocation = () => remote.dialog.showOpenDialog({properties: ['openDirectory']}); + +const saveFile = (path, name, data, encoding = 'utf-8') => { + return new Promise(resolve => { + try { + fs.writeFileSync(`${path}/${name}`, data, encoding); + resolve(true); + } + catch(e) { + console.error('Error saving file', e); + resolve(false); + } + }) +}; + +module.exports = { + getFileLocation, + getFolderLocation, + saveFile +} \ No newline at end of file diff --git a/electron/services/notifier.js b/electron/services/notifier.js new file mode 100644 index 00000000..352bd911 --- /dev/null +++ b/electron/services/notifier.js @@ -0,0 +1,17 @@ +const notifier = require("node-notifier"); +const {icon} = require('../utils'); + +class NotificationService { + static pushNotification(title, body){ + notifier.notify({ + message:body, + title, + appID:'com.get-scatter.server', + sound: false, + icon, + wait:false + }); + } +} + +module.exports = NotificationService; \ No newline at end of file diff --git a/electron/services/sockets.js b/electron/services/sockets.js new file mode 100644 index 00000000..80eb984b --- /dev/null +++ b/electron/services/sockets.js @@ -0,0 +1,162 @@ +const http = require('http'); +const https = require('https'); +const WebSocket = require('ws'); +const net = require('net'); +const {isDev} = require('../utils'); + +let mainWindow; + +class LowLevelSocketService { + + constructor(){ + this.rekeyPromise = null; + this.openConnections = {}; + this.websockets = []; + this.ports = {}; + } + + static setWindow(w){ + mainWindow = w; + } + + async getNewKey(origin, id){ + return new Promise((resolve, reject) => { + this.rekeyPromise = {resolve, reject}; + this.emit(origin, id, 'rekey'); + return this.rekeyPromise; + }) + } + + async emit(origin, id, path, data){ + const socket = this.openConnections[origin+id]; + return this.emitSocket(socket, path, data); + } + + async emitSocket(socket, path, data){ + if(!socket) return console.error('No socket found'); + socket.send('42/scatter,' + JSON.stringify([path, data ? data : false])) + } + + async initialize(_certs){ + + const socketHandler = socket => { + let origin = null; + + socket.send("40"); + socket.send("40/scatter"); + socket.send(`42/scatter,["connected"]`); + + const id = Math.round(Math.random() * 999999999).toString(); + + // Just logging errors for debugging purposes (dev only) + if(isDev) socket.on('error', async request => console.log('error', request)); + + // Different clients send different message types for disconnect (ws vs socket.io) + socket.on('close', () => delete this.openConnections[origin+id]); + socket.on('disconnect', () => delete this.openConnections[origin+id]); + + socket.on('message', msg => { + if(msg.indexOf('42/scatter') === -1) return false; + const [type, request] = JSON.parse(msg.replace('42/scatter,', '')); + + const killRequest = () => this.emitSocket(socket, 'api', {id:request.id, result:null}); + + if(!request.plugin || request.plugin.length > 100) return killRequest(); + request.plugin = request.plugin.replace(/\s/g, ""); + + if(request.plugin.trim().toLowerCase() === 'Scatter') return killRequest(); + if(request.data.hasOwnProperty('payload') && request.data.payload.origin.trim().toLowerCase() === 'Scatter') return killRequest(); + + let requestOrigin; + if(request.data.hasOwnProperty('payload')) requestOrigin = request.data.payload.origin; + else requestOrigin = request.data.origin; + + if(!origin) origin = requestOrigin; + else if(origin && requestOrigin !== origin) return this.emitSocket(socket, 'api', {id:request.id, result:null}); + if(!this.openConnections.hasOwnProperty(origin+id)) this.openConnections[origin+id] = socket; + + switch(type){ + case 'pair': return mainWindow.webContents.send('pair', {request, id}); + case 'rekeyed': return this.rekeyPromise.resolve(request); + case 'api': return mainWindow.webContents.send('api', {request, id}); + } + + }); + } + + if(this.websockets.length) return this.websockets; + + await this.findOpenPorts(); + mainWindow.webContents.send('ports', this.ports); + + const requestHandler = (_, res) => { + res.setHeader('Access-Control-Allow-Origin', '*'); + res.setHeader('Access-Control-Request-Method', '*'); + res.setHeader('Access-Control-Allow-Methods', 'OPTIONS, GET'); + res.setHeader('Access-Control-Allow-Headers', '*'); + res.setHeader('Content-Type', 'application/json'); + res.end('scatter'); + } + + await Promise.all(Object.keys(this.ports).map(async port => { + const server = this.ports[port] ? https.createServer(_certs, requestHandler) : http.createServer(requestHandler); + this.websockets.push(new WebSocket.Server({ server })); + server.listen(port); + + return true; + })); + + this.websockets.map(ws => ws.on('connection', socketHandler)); + return this.websockets; + } + + async close(){ + this.websockets.map(ws => { + if(typeof ws.clients.map === 'function') ws.clients.map(ws => ws.terminate()); + }) + + return true; + } + + sendEvent(event, payload, origin){ + const sockets = Object.keys(this.openConnections).filter(x => x.indexOf(origin) === 0).map(x => this.openConnections[x]); + sockets.map(x => this.emitSocket(x, 'event', {event, payload})); + return true; + } + + broadcastEvent(event, payload){ + Object.keys(this.openConnections).map(origin => this.sendEvent(event, payload, origin)); + return true; + } + + async findOpenPorts(){ + const isPortAvailable = (port = 0) => { + return new Promise(async resolve => { + const server = net.createServer(); + + server.once('error', err => resolve(err.code !== 'EADDRINUSE')); + + server.once('listening', () => { + server.close(); + resolve(true); + }); + + server.listen(port); + }) + } + + const findPort = async (delta=0) => { + let port = 50005+delta; + while(!await isPortAvailable(port)) port+=1500; + return port; + }; + + const http = await findPort(); + const https = await findPort(1); + this.ports = {[http]:false, [https]:true}; + return true; + } + +} + +module.exports = LowLevelSocketService; \ No newline at end of file diff --git a/electron/services/storage.js b/electron/services/storage.js new file mode 100644 index 00000000..c2fb4ef3 --- /dev/null +++ b/electron/services/storage.js @@ -0,0 +1,96 @@ +const Store = require('electron-store'); +const {saveFile} = require('./files') + +const ABIS_NAME = 'abi'; +const HISTORIES_NAME = 'histories'; +const TRANSLATION_NAME = 'translation'; +const SCATTER_DATA_NAME = 'scatter'; +const SCATTER_INTERMED_NAME = 'scatter_intermed'; + +const stores = {}; +const getStore = name => { + if(!stores.hasOwnProperty(name)) + stores[name] = new Store({name}); + + return stores[name]; +}; + +const scatterStorage = () => getStore(SCATTER_DATA_NAME); +const historyStorage = () => getStore(HISTORIES_NAME); +const translationStorage = () => getStore(TRANSLATION_NAME); +const scatterIntermedStorage = () => getStore(SCATTER_INTERMED_NAME); +const abiStorage = () => getStore(ABIS_NAME); + +const electron = require('electron'); +console.log('electron', electron); +const {app} = electron; +const fs = require('fs'); + + + +let saveResolvers = []; +let saveTimeouts = []; +const clearSaveTimeouts = () => { + saveResolvers.map(x => x(true)); + saveTimeouts.map(x => clearTimeout(x)); + saveTimeouts = []; +}; + +const safeSetScatter = async (scatter, resolver) => { + const path = name => `${app.getPath('userData')}/${name}.json`; + const retry = () => saveTimeouts.push(setTimeout(() => safeSetScatter(scatter, resolver), 50)); + + const salt = await getSalt(); + await scatterIntermedStorage().set('scatter', scatter); + await scatterIntermedStorage().set('salt', salt); + const savedScatter = await scatterIntermedStorage().get('scatter'); + + // Didn't save properly + if(scatter !== savedScatter) retry(); + + // Saved properly, overwriting old data with new data + else fs.rename(path(SCATTER_INTERMED_NAME), path(SCATTER_DATA_NAME), (err) => { + if(err) return retry(); + resolver(true); + }); +}; + +const isPopup = typeof location === 'undefined' ? false : location.hash.indexOf('popout') > -1; + + +const getScatter = () => scatterStorage().get('scatter'); +const setScatter = (scatter) => { + if(isPopup) return; + return new Promise(async resolve => { + clearSaveTimeouts(); + saveResolvers.push(resolve); + safeSetScatter(scatter, resolve); + }) +} + +const removeScatter = () => { + if(isPopup) return; + scatterStorage().clear(); + abiStorage().clear(); + historyStorage().clear(); + translationStorage().clear(); + // StoreService.get().commit(Actions.SET_SCATTER, null); + // window.localStorage.removeItem('scatter'); + // ipcFaF('key', null); + return true; +} + +const getSalt = () => { + console.log('getting salt') + return scatterStorage().get('salt') || 'SALT_ME'; +} +const setSalt = (salt) => scatterStorage().set('salt', salt); + + +module.exports = { + getScatter, + setScatter, + removeScatter, + getSalt, + setSalt, +} \ No newline at end of file diff --git a/electron/services/wallet.js b/electron/services/wallet.js new file mode 100644 index 00000000..2353c0cb --- /dev/null +++ b/electron/services/wallet.js @@ -0,0 +1,113 @@ +const LowLevelWindowService = require("./windows"); + +const {ipcMain} = require("electron"); + +const bip39 = require('bip39'); +const scrypt = require('scrypt-async'); +const AES = require("aes-oop").default; +const storage = require('./storage') +const path = require('path') +const Scatter = require('@walletpack/core/models/Scatter').default; + + + +let seed, salt; +let scatter = storage.getScatter(); + +salt = storage.getSalt(); + +const setScatter = (_s) => scatter = JSON.parse(JSON.stringify(_s)); +const getScatter = () => JSON.parse(JSON.stringify(scatter)); + +const updateScatter = async (_s) => { + const isEncrypted = x => x.toString().indexOf('"iv":') > -1 + + _s.keychain.keypairs.map(x => { + if(!isEncrypted(x.privateKey)){ + x.privateKey = AES.encrypt(x.privateKey, seed); + } + }) + + _s.keychain.identities.map(x => { + if(!isEncrypted(x.privateKey)){ + x.privateKey = AES.encrypt(x.privateKey, seed); + } + }) + + _s.keychain.cards.map(x => { + if(!isEncrypted(x.secure)){ + x.secure = AES.encrypt(x.secure, seed); + } + }); + + scatter = JSON.parse(JSON.stringify(_s)); + + _s.keychain = AES.encrypt(_s.keychain, seed); + await storage.setScatter(AES.encrypt(_s, seed)); + return getScatter(); +} + + + + +const hashPassword = (password) => { + return new Promise(async resolve => { + scrypt(password, salt, { + N: 16384, + r: 8, + p: 1, + dkLen: 16, + encoding: 'hex' + }, (derivedKey) => { + resolve(derivedKey); + }) + }); +} + +const passwordToSeed = async password => { + const hash = await hashPassword(password); + let mnemonic = bip39.entropyToMnemonic(hash); + return bip39.mnemonicToSeedHex(mnemonic); +} + + + + +const unlock = async password => { + try { + seed = await passwordToSeed(password); + const decrypted = AES.decrypt(scatter, seed); + if(!decrypted.hasOwnProperty('keychain')) return false; + decrypted.keychain = AES.decrypt(decrypted.keychain, seed); + scatter = decrypted; + + setTimeout(() => { + LowLevelWindowService.queuePopup(); + }, 1000); + + return getScatter(); + } catch(e){ + console.error('decrypt error', e); + seed = null; + scatter = storage.getScatter(); + return false; + } +} + +const sign = () => { + +}; + +ipcMain.on('load', (e) => { + e.sender.send('loaded', getScatter()); +}) + + + +module.exports = { + updateScatter, + setScatter, + getScatter, + sign, + unlock, +} \ No newline at end of file diff --git a/electron/services/windows.js b/electron/services/windows.js new file mode 100644 index 00000000..dad7248b --- /dev/null +++ b/electron/services/windows.js @@ -0,0 +1,89 @@ + +const electron = require('electron'); +const {Menu, BrowserWindow} = electron; +const {mainUrl} = require('../utils') + +const isMac = () => process.platform === 'darwin'; +let waitingPopup; +class LowLevelWindowService { + + static getWindow(width = 800, height = 600){ + return new Promise(resolve => { + const win = new BrowserWindow({ + backgroundColor:'#FFFFFF', + width, height, + frame: false, radii: [5,5,5,5], + icon:'assets/icon.png', + show:false, + webPreferences:{ + nodeIntegration:true, + webviewTag:true, + } }); + win.loadURL(mainUrl(true)); + // win.loadURL('http://localhost:8081/#/popout'); + resolve(win) + // win.once('ready-to-show', () => resolve(win)); + }) + } + + static async queuePopup(){ + setTimeout(async () => { + waitingPopup = await this.getWindow(800,600); + }, 100); + } + + static async openPopOut(popup, onClosed = () => {}, width = 800, height = 600, dontHide = false){ + let win = waitingPopup; + if(!win) win = await this.getWindow(); + else waitingPopup = null; + + win.webContents.send('ready', popup); + + win.setSize(width, height); + + // Getting the screen to display the popup based on + // where the user is at the time ( for dual monitors ) + const mousePoint = electron.screen.getCursorScreenPoint(); + const activeDisplay = electron.screen.getDisplayNearestPoint(mousePoint); + let {width:screenWidth, height:screenHeight} = activeDisplay.workAreaSize; + const leftBound = activeDisplay.bounds.x; + + let bounds = electron.screen.getPrimaryDisplay().bounds; + let x = bounds.x + (leftBound + ((bounds.width - width) / 2)); + let y = bounds.y + ((bounds.height - height) / 2); + win.setPosition(Math.round(x),Math.round(y)); + + win.once('closed', async () => { + // This is a fix for MacOS systems which causes the + // main window to always pop up after popups closing. + if (!dontHide && isMac()) { + // mainWindow.hide(); + Menu.sendActionToFirstResponder('hide:'); + // mainWindow.show(); + } + + onClosed(win); + win = null; + }); + + this.queuePopup(); + + win.show(); + win.setAlwaysOnTop(true, "floating"); + win.focus(); + + + + if(isMac()){ + electron.app.dock.hide(); + win.setAlwaysOnTop(false); + win.setVisibleOnAllWorkspaces(true); + win.setFullScreenable(false); + electron.app.dock.show(); + } + + return win; + } +} + +module.exports = LowLevelWindowService; diff --git a/electron/utils.js b/electron/utils.js new file mode 100644 index 00000000..e4fcb31a --- /dev/null +++ b/electron/utils.js @@ -0,0 +1,27 @@ +const path = require("path"); +const url = require("url"); + +const isDev = process.mainModule.filename.indexOf('app.asar') === -1; + +let icon = isDev + ? 'static/icons/icon.png' + : __dirname + '/static/icons/icon.png'; + +let trayIcon = isDev + ? 'static/icons/icon-tray.png' + : __dirname + '/static/icons/icon-tray.png'; + +let mainUrl = isPopup => isDev ? `http://localhost:8080/${isPopup ? '/#/popout' : ''}` : url.format({ + pathname: path.join(__dirname, "dist", "index.html"), + protocol: "file:", + slashes: true, + hash:isPopup ? '/popout' : null +}); + +module.exports = { + isDev, + mainUrl, + icon, + trayIcon, + +} \ No newline at end of file diff --git a/package.json b/package.json index a4f82d24..11cb4bc0 100644 --- a/package.json +++ b/package.json @@ -25,15 +25,16 @@ "@babel/runtime": "^7.5.4", "@ledgerhq/hw-app-eth": "^4.68.4", "@ledgerhq/hw-transport-node-hid": "^4.55.0", - "@walletpack/bitcoin": "^1.0.19", - "@walletpack/core": "^1.0.18", - "@walletpack/eosio": "^0.0.19", - "@walletpack/ethereum": "^0.0.19", - "@walletpack/tron": "^0.0.19", + "@walletpack/bitcoin": "^1.0.20", + "@walletpack/core": "^1.0.19", + "@walletpack/eosio": "^0.0.20", + "@walletpack/ethereum": "^0.0.20", + "@walletpack/tron": "^0.0.20", "aes-oop": "^1.0.4", "asn1-ber": "^1.0.9", "babel-polyfill": "^6.26.0", "bip32-path": "^0.4.2", + "bip39": "^2.6.0", "chartist": "^0.11.0", "device-uuid": "^1.0.4", "electron-store": "^3.2.0", @@ -46,6 +47,7 @@ "murmurhash": "^0.0.2", "node-notifier": "^5.3.0", "ridl": "^2.0.47", + "scrypt-async": "^2.0.1", "vue-markdown": "^2.2.4", "ws": "^7.0.0" }, diff --git a/preload.js b/preload.js index 85b06c17..5e5d2a4e 100644 --- a/preload.js +++ b/preload.js @@ -1,6 +1,22 @@ const { ipcRenderer: ipc, remote } = require('electron'); let send = remote.getGlobal('scatterMessage'); +let sendLog = remote.getGlobal('scatterLog'); +console.log('preload', location); ipc.on('scatter', (event, data) => window.ScatterWallet.received(data)); ipc.on('sockets', (event, data) => window.ScatterWallet.sockets(data)); -window.ScatterWallet = { send }; \ No newline at end of file +ipc.on('popout', (event, data) => window.ScatterWallet.popout(data)); +window.ScatterWallet = { send, windowId:remote.getCurrentWindow().id }; + + +const log = console.log; +console.log = (...params) => { + sendLog(params); + return log(...params); +} + +const logerr = console.error; +console.error = (...params) => { + sendLog(params); + return logerr(...params); +} \ No newline at end of file diff --git a/src/components/Popups.vue b/src/components/Popups.vue index 6f1e250c..c157e77b 100644 --- a/src/components/Popups.vue +++ b/src/components/Popups.vue @@ -4,49 +4,10 @@
-
- - - - - - - - - - - - - - +
- - -
-
-
-
-
- - - - - - - - - - - - - - - - -
-
@@ -70,83 +31,14 @@ import {PopupDisplayTypes, PopupTypes, isFullscreen} from '../models/popups/Popup' import Snackbar from './popins/overlay/Snackbar.vue' - import TransactionSuccess from './popins/overlay/TransactionSuccess.vue' - - import EosProxyVotes from './popins/fullscreen/EosProxyVotes' - import EosChangePermissions from './popins/fullscreen/EosChangePermissions' - import EosModerateRam from './popins/fullscreen/EosModerateRam' - import EosModerateCpuNet from './popins/fullscreen/EosModerateCpuNet' - import ConfirmPassword from '../components/popins/fullscreen/ConfirmPassword' - import UnlinkAccount from '../components/popins/fullscreen/UnlinkAccount' - import EosCreateAccount from "./popins/fullscreen/EosCreateAccount"; - import AddCustomNetwork from "./popins/fullscreen/AddCustomNetwork"; - import AddNewContact from "./popins/fullscreen/AddNewContact"; - import ImportKeypair from "./popins/fullscreen/ImportKeypair"; - import GenerateKeypair from "./popins/fullscreen/GenerateKeypair"; - import RemoveKeypair from "./popins/fullscreen/RemoveKeypair"; - import SelectAccount from "./popins/overlay/SelectAccount"; - import SelectKeypair from "./popins/overlay/SelectKeypair"; - import Prompt from "./popins/overlay/Prompt"; - import SelectToken from "./popins/overlay/SelectToken"; - import SelectTokenAndAccount from "./popins/overlay/SelectTokenAndAccount"; - import CheckHardware from "./popins/fullscreen/CheckHardware"; - import DestroyScatter from "./popins/fullscreen/DestroyScatter"; - import ImportBackup from "./popins/fullscreen/ImportBackup"; - import ConfirmExchange from "./popins/overlay/ConfirmExchange"; - import ConfirmTransfer from "./popins/overlay/ConfirmTransfer"; - // import DisplayToken from "./popins/fullscreen/DisplayToken"; - import UpdateAvailable from "./popins/overlay/UpdateAvailable"; - import SelectRecipient from "./popins/overlay/SelectRecipient"; - import SelectBlockchain from "./popins/overlay/SelectBlockchain"; - import EnterSecurityCode from "./popins/fullscreen/EnterSecurityCode"; - import SelectFromList from "./popins/overlay/SelectFromList"; - import ExportPrivateKey from "./popins/fullscreen/ExportPrivateKey"; - import EosLinkAccount from "./popins/overlay/EosLinkAccount"; - import SelectDisplayToken from "./popins/overlay/SelectDisplayToken"; - import Terms from "./popins/fullscreen/Terms"; - import ChangeIdentityKey from "./popins/fullscreen/ChangeIdentityKey"; - import EnterPIN from "./popins/overlay/EnterPIN"; import * as UIActions from "../store/ui_actions"; export default { components:{ - EnterPIN, - ChangeIdentityKey, - Terms, - SelectDisplayToken, - EosLinkAccount, - ExportPrivateKey, - SelectFromList, - EnterSecurityCode, - SelectRecipient, - SelectBlockchain, - SelectTokenAndAccount, - SelectAccount, - SelectKeypair, - SelectToken, - UpdateAvailable, + Terms:() => import("./popins/fullscreen/Terms"), + DestroyScatter:() => import("./popins/fullscreen/DestroyScatter"), + ImportBackup:() => import("./popins/fullscreen/ImportBackup"), Snackbar, - TransactionSuccess, - Prompt, - ImportBackup, - DestroyScatter, - // RemoveLocation, - AddCustomNetwork, - AddNewContact, - ConfirmPassword, - EosChangePermissions, - EosProxyVotes, - EosModerateRam, - EosModerateCpuNet, - EosCreateAccount, - UnlinkAccount, - RemoveKeypair, - ImportKeypair, - GenerateKeypair, - CheckHardware, - ConfirmExchange, - ConfirmTransfer, - // DisplayToken, }, data(){ return { popupTypes:PopupTypes, diff --git a/src/components/Processes.vue b/src/components/Processes.vue deleted file mode 100644 index 0646ff42..00000000 --- a/src/components/Processes.vue +++ /dev/null @@ -1,119 +0,0 @@ - - - - - diff --git a/src/components/QuickActions.vue b/src/components/QuickActions.vue deleted file mode 100644 index aab121bf..00000000 --- a/src/components/QuickActions.vue +++ /dev/null @@ -1,263 +0,0 @@ - - - - - \ No newline at end of file diff --git a/src/components/Sidebar.vue b/src/components/Sidebar.vue deleted file mode 100644 index ae87d7be..00000000 --- a/src/components/Sidebar.vue +++ /dev/null @@ -1,267 +0,0 @@ - - - - - \ No newline at end of file diff --git a/src/components/TokenBar.vue b/src/components/TokenBar.vue deleted file mode 100644 index 41f592d1..00000000 --- a/src/components/TokenBar.vue +++ /dev/null @@ -1,124 +0,0 @@ - - - - - \ No newline at end of file diff --git a/src/components/ViewBase.vue b/src/components/ViewBase.vue index 71416935..b94b3292 100644 --- a/src/components/ViewBase.vue +++ b/src/components/ViewBase.vue @@ -2,12 +2,10 @@
- +
+ -
- - -
+
@@ -43,9 +41,9 @@ ...mapGetters([ 'unlocked', ]), - isPopout(){ - return this.$route.name === 'popout'; - }, + isPopOut(){ + return this.$route.name === RouteNames.POP_OUT + } }, mounted(){ @@ -87,8 +85,9 @@ margin-top:40px; box-shadow:inset 0 0 0 1px $lightgrey; - &.no-menu { - + &.popout { + height:100vh; + margin-top:0; } } diff --git a/src/components/login/SetPassword.vue b/src/components/login/SetPassword.vue index bb180a53..96a75b15 100644 --- a/src/components/login/SetPassword.vue +++ b/src/components/login/SetPassword.vue @@ -21,7 +21,6 @@ - - \ No newline at end of file diff --git a/src/components/misc/Card.vue b/src/components/misc/Card.vue deleted file mode 100644 index 48ad1389..00000000 --- a/src/components/misc/Card.vue +++ /dev/null @@ -1,153 +0,0 @@ - - - - - \ No newline at end of file diff --git a/src/components/misc/Contacts.vue b/src/components/misc/Contacts.vue deleted file mode 100644 index 62ffe225..00000000 --- a/src/components/misc/Contacts.vue +++ /dev/null @@ -1,167 +0,0 @@ - - - - - \ No newline at end of file diff --git a/src/components/misc/CreditCardsList.vue b/src/components/misc/CreditCardsList.vue deleted file mode 100644 index 8d22a708..00000000 --- a/src/components/misc/CreditCardsList.vue +++ /dev/null @@ -1,54 +0,0 @@ - - - - - \ No newline at end of file diff --git a/src/components/misc/EditContact.vue b/src/components/misc/EditContact.vue deleted file mode 100644 index 1f0f652c..00000000 --- a/src/components/misc/EditContact.vue +++ /dev/null @@ -1,91 +0,0 @@ - - - - - \ No newline at end of file diff --git a/src/components/misc/EditNetwork.vue b/src/components/misc/EditNetwork.vue deleted file mode 100644 index 8b77a231..00000000 --- a/src/components/misc/EditNetwork.vue +++ /dev/null @@ -1,182 +0,0 @@ - - - - - \ No newline at end of file diff --git a/src/components/misc/GetRIDL.vue b/src/components/misc/GetRIDL.vue deleted file mode 100644 index 899b6e99..00000000 --- a/src/components/misc/GetRIDL.vue +++ /dev/null @@ -1,271 +0,0 @@ - - - - - \ No newline at end of file diff --git a/src/components/misc/KeysAndAccountList.vue b/src/components/misc/KeysAndAccountList.vue deleted file mode 100644 index ead01cf3..00000000 --- a/src/components/misc/KeysAndAccountList.vue +++ /dev/null @@ -1,495 +0,0 @@ - - - - - \ No newline at end of file diff --git a/src/components/panels/keypair/ImportHardwareKey.vue b/src/components/panels/keypair/ImportHardwareKey.vue deleted file mode 100644 index 1697e3cb..00000000 --- a/src/components/panels/keypair/ImportHardwareKey.vue +++ /dev/null @@ -1,198 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/src/components/panels/keypair/ImportPrivateKey.vue b/src/components/panels/keypair/ImportPrivateKey.vue deleted file mode 100644 index 091a545e..00000000 --- a/src/components/panels/keypair/ImportPrivateKey.vue +++ /dev/null @@ -1,53 +0,0 @@ - - - - - \ No newline at end of file diff --git a/src/components/panels/keypair/ImportQRKey.vue b/src/components/panels/keypair/ImportQRKey.vue deleted file mode 100644 index 1e9e60de..00000000 --- a/src/components/panels/keypair/ImportQRKey.vue +++ /dev/null @@ -1,67 +0,0 @@ - - - - - \ No newline at end of file diff --git a/src/components/panels/settings/SettingsBackup.vue b/src/components/panels/settings/SettingsBackup.vue deleted file mode 100644 index 12d0bd1a..00000000 --- a/src/components/panels/settings/SettingsBackup.vue +++ /dev/null @@ -1,76 +0,0 @@ - - - - - \ No newline at end of file diff --git a/src/components/panels/settings/SettingsDestroy.vue b/src/components/panels/settings/SettingsDestroy.vue deleted file mode 100644 index e2d72daf..00000000 --- a/src/components/panels/settings/SettingsDestroy.vue +++ /dev/null @@ -1,50 +0,0 @@ - - - - - diff --git a/src/components/panels/settings/SettingsExplorer.vue b/src/components/panels/settings/SettingsExplorer.vue deleted file mode 100644 index bf90727c..00000000 --- a/src/components/panels/settings/SettingsExplorer.vue +++ /dev/null @@ -1,78 +0,0 @@ - - - - - \ No newline at end of file diff --git a/src/components/panels/settings/SettingsFirewall.vue b/src/components/panels/settings/SettingsFirewall.vue deleted file mode 100644 index beabb44f..00000000 --- a/src/components/panels/settings/SettingsFirewall.vue +++ /dev/null @@ -1,165 +0,0 @@ - - - - - diff --git a/src/components/panels/settings/SettingsGeneral.vue b/src/components/panels/settings/SettingsGeneral.vue deleted file mode 100644 index a26a4b6c..00000000 --- a/src/components/panels/settings/SettingsGeneral.vue +++ /dev/null @@ -1,180 +0,0 @@ - - - - - diff --git a/src/components/panels/settings/SettingsPassword.vue b/src/components/panels/settings/SettingsPassword.vue deleted file mode 100644 index 7712e555..00000000 --- a/src/components/panels/settings/SettingsPassword.vue +++ /dev/null @@ -1,123 +0,0 @@ - - - - - diff --git a/src/components/panels/settings/SettingsTokens.vue b/src/components/panels/settings/SettingsTokens.vue deleted file mode 100644 index aa6f3e8f..00000000 --- a/src/components/panels/settings/SettingsTokens.vue +++ /dev/null @@ -1,325 +0,0 @@ - - - - - \ No newline at end of file diff --git a/src/components/popins/fullscreen/AddCustomNetwork.vue b/src/components/popins/fullscreen/AddCustomNetwork.vue deleted file mode 100644 index 88e18165..00000000 --- a/src/components/popins/fullscreen/AddCustomNetwork.vue +++ /dev/null @@ -1,63 +0,0 @@ - - - - - \ No newline at end of file diff --git a/src/components/popins/fullscreen/AddNewContact.vue b/src/components/popins/fullscreen/AddNewContact.vue deleted file mode 100644 index 8d37e1a2..00000000 --- a/src/components/popins/fullscreen/AddNewContact.vue +++ /dev/null @@ -1,70 +0,0 @@ - - - - - \ No newline at end of file diff --git a/src/components/popins/fullscreen/ChangeIdentityKey.vue b/src/components/popins/fullscreen/ChangeIdentityKey.vue deleted file mode 100644 index a0cdbe5a..00000000 --- a/src/components/popins/fullscreen/ChangeIdentityKey.vue +++ /dev/null @@ -1,109 +0,0 @@ - - - - - \ No newline at end of file diff --git a/src/components/popins/fullscreen/CheckHardware.vue b/src/components/popins/fullscreen/CheckHardware.vue deleted file mode 100644 index c8be744e..00000000 --- a/src/components/popins/fullscreen/CheckHardware.vue +++ /dev/null @@ -1,63 +0,0 @@ - - - - - \ No newline at end of file diff --git a/src/components/popins/fullscreen/ConfirmPassword.vue b/src/components/popins/fullscreen/ConfirmPassword.vue deleted file mode 100644 index c4c808bb..00000000 --- a/src/components/popins/fullscreen/ConfirmPassword.vue +++ /dev/null @@ -1,80 +0,0 @@ - - - - - \ No newline at end of file diff --git a/src/components/popins/fullscreen/DestroyScatter.vue b/src/components/popins/fullscreen/DestroyScatter.vue index 99e470ac..61fb2eb2 100644 --- a/src/components/popins/fullscreen/DestroyScatter.vue +++ b/src/components/popins/fullscreen/DestroyScatter.vue @@ -2,10 +2,10 @@
-
{{locale(langKeys.POPINS.FULLSCREEN.DESTROY.Title)}}
+
You are about to Destroy your Scatter
-
{{locale(langKeys.POPINS.FULLSCREEN.DESTROY.Disclaimer)}}
-
{{locale(langKeys.POPINS.FULLSCREEN.DESTROY.Desc)}}
+
This action is dangerous!
+
Make sure you have all of your keys before doing this, or you will lose them forever.
diff --git a/src/components/popins/fullscreen/EnterSecurityCode.vue b/src/components/popins/fullscreen/EnterSecurityCode.vue deleted file mode 100644 index 7a62bef6..00000000 --- a/src/components/popins/fullscreen/EnterSecurityCode.vue +++ /dev/null @@ -1,78 +0,0 @@ - - - - - \ No newline at end of file diff --git a/src/components/popins/fullscreen/EosChangePermissions.vue b/src/components/popins/fullscreen/EosChangePermissions.vue deleted file mode 100644 index fec2bad9..00000000 --- a/src/components/popins/fullscreen/EosChangePermissions.vue +++ /dev/null @@ -1,131 +0,0 @@ - - - - - \ No newline at end of file diff --git a/src/components/popins/fullscreen/EosCreateAccount.vue b/src/components/popins/fullscreen/EosCreateAccount.vue deleted file mode 100644 index 9e9e36f6..00000000 --- a/src/components/popins/fullscreen/EosCreateAccount.vue +++ /dev/null @@ -1,430 +0,0 @@ - - - - - \ No newline at end of file diff --git a/src/components/popins/fullscreen/EosModerateCpuNet.vue b/src/components/popins/fullscreen/EosModerateCpuNet.vue deleted file mode 100644 index 9e853b4b..00000000 --- a/src/components/popins/fullscreen/EosModerateCpuNet.vue +++ /dev/null @@ -1,236 +0,0 @@ - - - - - \ No newline at end of file diff --git a/src/components/popins/fullscreen/EosModerateRam.vue b/src/components/popins/fullscreen/EosModerateRam.vue deleted file mode 100644 index cd059d2c..00000000 --- a/src/components/popins/fullscreen/EosModerateRam.vue +++ /dev/null @@ -1,237 +0,0 @@ - - - - - \ No newline at end of file diff --git a/src/components/popins/fullscreen/EosProxyVotes.vue b/src/components/popins/fullscreen/EosProxyVotes.vue deleted file mode 100644 index 844fd072..00000000 --- a/src/components/popins/fullscreen/EosProxyVotes.vue +++ /dev/null @@ -1,220 +0,0 @@ - - - - - \ No newline at end of file diff --git a/src/components/popins/fullscreen/ExportPrivateKey.vue b/src/components/popins/fullscreen/ExportPrivateKey.vue deleted file mode 100644 index 63183ff6..00000000 --- a/src/components/popins/fullscreen/ExportPrivateKey.vue +++ /dev/null @@ -1,287 +0,0 @@ - - - - - \ No newline at end of file diff --git a/src/components/popins/fullscreen/GenerateKeypair.vue b/src/components/popins/fullscreen/GenerateKeypair.vue deleted file mode 100644 index 9493f4c2..00000000 --- a/src/components/popins/fullscreen/GenerateKeypair.vue +++ /dev/null @@ -1,98 +0,0 @@ - - - - - \ No newline at end of file diff --git a/src/components/popins/fullscreen/ImportBackup.vue b/src/components/popins/fullscreen/ImportBackup.vue index dafd24b5..abb21af0 100644 --- a/src/components/popins/fullscreen/ImportBackup.vue +++ b/src/components/popins/fullscreen/ImportBackup.vue @@ -3,9 +3,9 @@
-

{{locale(langKeys.LOGIN.RESTORE.Title)}}

+

Import a Scatter Backup

- {{locale(langKeys.LOGIN.RESTORE.SubTitle)}} + Scatter backups are .json files that allow you to recover your full Scatter data.


@@ -54,7 +54,8 @@ }, importBackup(){ const unrestore = () => { - this.setWorkingScreen(false); + // TODO: + // this.setWorkingScreen(false); this.restoringBackup = false; } if(this.restoringBackup) return; diff --git a/src/components/popins/fullscreen/ImportKeypair.vue b/src/components/popins/fullscreen/ImportKeypair.vue deleted file mode 100644 index e3f27a91..00000000 --- a/src/components/popins/fullscreen/ImportKeypair.vue +++ /dev/null @@ -1,238 +0,0 @@ - - - - - \ No newline at end of file diff --git a/src/components/popins/fullscreen/RemoveKeypair.vue b/src/components/popins/fullscreen/RemoveKeypair.vue deleted file mode 100644 index 90b0354e..00000000 --- a/src/components/popins/fullscreen/RemoveKeypair.vue +++ /dev/null @@ -1,84 +0,0 @@ - - - - - \ No newline at end of file diff --git a/src/components/popins/fullscreen/UnlinkAccount.vue b/src/components/popins/fullscreen/UnlinkAccount.vue deleted file mode 100644 index d56f70ad..00000000 --- a/src/components/popins/fullscreen/UnlinkAccount.vue +++ /dev/null @@ -1,98 +0,0 @@ - - - - - \ No newline at end of file diff --git a/src/components/popins/overlay/ConfirmExchange.vue b/src/components/popins/overlay/ConfirmExchange.vue deleted file mode 100644 index 944cd0b3..00000000 --- a/src/components/popins/overlay/ConfirmExchange.vue +++ /dev/null @@ -1,63 +0,0 @@ - - - - - \ No newline at end of file diff --git a/src/components/popins/overlay/ConfirmTransfer.vue b/src/components/popins/overlay/ConfirmTransfer.vue deleted file mode 100644 index 545f0f0b..00000000 --- a/src/components/popins/overlay/ConfirmTransfer.vue +++ /dev/null @@ -1,59 +0,0 @@ - - - - - \ No newline at end of file diff --git a/src/components/popins/overlay/EnterPIN.vue b/src/components/popins/overlay/EnterPIN.vue deleted file mode 100644 index 3e4737be..00000000 --- a/src/components/popins/overlay/EnterPIN.vue +++ /dev/null @@ -1,63 +0,0 @@ - - - - - \ No newline at end of file diff --git a/src/components/popins/overlay/EosLinkAccount.vue b/src/components/popins/overlay/EosLinkAccount.vue deleted file mode 100644 index 3c120da8..00000000 --- a/src/components/popins/overlay/EosLinkAccount.vue +++ /dev/null @@ -1,125 +0,0 @@ - - - - - \ No newline at end of file diff --git a/src/components/popins/overlay/Prompt.vue b/src/components/popins/overlay/Prompt.vue deleted file mode 100644 index 5eaf0f23..00000000 --- a/src/components/popins/overlay/Prompt.vue +++ /dev/null @@ -1,84 +0,0 @@ - - - - - \ No newline at end of file diff --git a/src/components/popins/overlay/SelectAccount.vue b/src/components/popins/overlay/SelectAccount.vue deleted file mode 100644 index f3ccce37..00000000 --- a/src/components/popins/overlay/SelectAccount.vue +++ /dev/null @@ -1,54 +0,0 @@ - - - - - \ No newline at end of file diff --git a/src/components/popins/overlay/SelectBlockchain.vue b/src/components/popins/overlay/SelectBlockchain.vue deleted file mode 100644 index 78a0fbbc..00000000 --- a/src/components/popins/overlay/SelectBlockchain.vue +++ /dev/null @@ -1,55 +0,0 @@ - - - - - \ No newline at end of file diff --git a/src/components/popins/overlay/SelectDisplayToken.vue b/src/components/popins/overlay/SelectDisplayToken.vue deleted file mode 100644 index 5944269e..00000000 --- a/src/components/popins/overlay/SelectDisplayToken.vue +++ /dev/null @@ -1,87 +0,0 @@ - - - - - \ No newline at end of file diff --git a/src/components/popins/overlay/SelectFromList.vue b/src/components/popins/overlay/SelectFromList.vue deleted file mode 100644 index d4367186..00000000 --- a/src/components/popins/overlay/SelectFromList.vue +++ /dev/null @@ -1,92 +0,0 @@ - - - - - \ No newline at end of file diff --git a/src/components/popins/overlay/SelectKeypair.vue b/src/components/popins/overlay/SelectKeypair.vue deleted file mode 100644 index 5cfb3c91..00000000 --- a/src/components/popins/overlay/SelectKeypair.vue +++ /dev/null @@ -1,52 +0,0 @@ - - - - - \ No newline at end of file diff --git a/src/components/popins/overlay/SelectRecipient.vue b/src/components/popins/overlay/SelectRecipient.vue deleted file mode 100644 index 3f84ed1d..00000000 --- a/src/components/popins/overlay/SelectRecipient.vue +++ /dev/null @@ -1,99 +0,0 @@ - - - - - \ No newline at end of file diff --git a/src/components/popins/overlay/SelectToken.vue b/src/components/popins/overlay/SelectToken.vue deleted file mode 100644 index 339a6a56..00000000 --- a/src/components/popins/overlay/SelectToken.vue +++ /dev/null @@ -1,98 +0,0 @@ - - - - - \ No newline at end of file diff --git a/src/components/popins/overlay/SelectTokenAndAccount.vue b/src/components/popins/overlay/SelectTokenAndAccount.vue deleted file mode 100644 index a01961d4..00000000 --- a/src/components/popins/overlay/SelectTokenAndAccount.vue +++ /dev/null @@ -1,54 +0,0 @@ - - - - - \ No newline at end of file diff --git a/src/components/popins/overlay/TransactionSuccess.vue b/src/components/popins/overlay/TransactionSuccess.vue deleted file mode 100644 index e6d15493..00000000 --- a/src/components/popins/overlay/TransactionSuccess.vue +++ /dev/null @@ -1,84 +0,0 @@ - - - - - \ No newline at end of file diff --git a/src/components/popins/overlay/UpdateAvailable.vue b/src/components/popins/overlay/UpdateAvailable.vue deleted file mode 100644 index eb87e4f7..00000000 --- a/src/components/popins/overlay/UpdateAvailable.vue +++ /dev/null @@ -1,100 +0,0 @@ - - - - - \ No newline at end of file diff --git a/src/components/popouts/PopOutApp.vue b/src/components/popouts/PopOutApp.vue deleted file mode 100644 index c5ccebfa..00000000 --- a/src/components/popouts/PopOutApp.vue +++ /dev/null @@ -1,127 +0,0 @@ - - - - - \ No newline at end of file diff --git a/src/components/popouts/PopOutHead.vue b/src/components/popouts/PopOutHead.vue deleted file mode 100644 index 67516294..00000000 --- a/src/components/popouts/PopOutHead.vue +++ /dev/null @@ -1,123 +0,0 @@ - - - - - diff --git a/src/components/popouts/RequiredFields.vue b/src/components/popouts/RequiredFields.vue deleted file mode 100644 index 58e3907e..00000000 --- a/src/components/popouts/RequiredFields.vue +++ /dev/null @@ -1,68 +0,0 @@ - - - - - \ No newline at end of file diff --git a/src/components/svgs/CreditCard.vue b/src/components/svgs/CreditCard.vue deleted file mode 100644 index 8cdee613..00000000 --- a/src/components/svgs/CreditCard.vue +++ /dev/null @@ -1,4 +0,0 @@ - \ No newline at end of file diff --git a/src/components/svgs/quick-actions/Exchange.vue b/src/components/svgs/quick-actions/Exchange.vue deleted file mode 100644 index 7f19a2a9..00000000 --- a/src/components/svgs/quick-actions/Exchange.vue +++ /dev/null @@ -1,3 +0,0 @@ - \ No newline at end of file diff --git a/src/components/svgs/quick-actions/Receive.vue b/src/components/svgs/quick-actions/Receive.vue deleted file mode 100644 index 797db58f..00000000 --- a/src/components/svgs/quick-actions/Receive.vue +++ /dev/null @@ -1,3 +0,0 @@ - \ No newline at end of file diff --git a/src/components/svgs/quick-actions/Refresh.vue b/src/components/svgs/quick-actions/Refresh.vue deleted file mode 100644 index a0f0848f..00000000 --- a/src/components/svgs/quick-actions/Refresh.vue +++ /dev/null @@ -1,60 +0,0 @@ - \ No newline at end of file diff --git a/src/components/svgs/quick-actions/Send.vue b/src/components/svgs/quick-actions/Send.vue deleted file mode 100644 index 645d7995..00000000 --- a/src/components/svgs/quick-actions/Send.vue +++ /dev/null @@ -1,3 +0,0 @@ - \ No newline at end of file diff --git a/src/components/tokens/TokenGraph.vue b/src/components/tokens/TokenGraph.vue deleted file mode 100644 index af0727f5..00000000 --- a/src/components/tokens/TokenGraph.vue +++ /dev/null @@ -1,188 +0,0 @@ - - - - - \ No newline at end of file diff --git a/src/components/tokens/TokenList.vue b/src/components/tokens/TokenList.vue deleted file mode 100644 index b6f30c91..00000000 --- a/src/components/tokens/TokenList.vue +++ /dev/null @@ -1,611 +0,0 @@ - - - - - \ No newline at end of file diff --git a/src/data/Countries.js b/src/data/Countries.js deleted file mode 100644 index 12b349a5..00000000 --- a/src/data/Countries.js +++ /dev/null @@ -1,246 +0,0 @@ -export default [ - {name: 'United States', code: 'US'}, - {name: 'Canada', code: 'CA'}, - {name: 'United Kingdom', code: 'GB'}, - {name: 'Australia', code: 'AU'}, - {name: 'Germany', code: 'DE'}, - {name: 'France', code: 'FR'}, - {name: 'Afghanistan', code: 'AF'}, - {name: 'Åland Islands', code: 'AX'}, - {name: 'Albania', code: 'AL'}, - {name: 'Algeria', code: 'DZ'}, - {name: 'American Samoa', code: 'AS'}, - {name: 'AndorrA', code: 'AD'}, - {name: 'Angola', code: 'AO'}, - {name: 'Anguilla', code: 'AI'}, - {name: 'Antarctica', code: 'AQ'}, - {name: 'Antigua and Barbuda', code: 'AG'}, - {name: 'Argentina', code: 'AR'}, - {name: 'Armenia', code: 'AM'}, - {name: 'Aruba', code: 'AW'}, - {name: 'Austria', code: 'AT'}, - {name: 'Azerbaijan', code: 'AZ'}, - {name: 'Bahamas', code: 'BS'}, - {name: 'Bahrain', code: 'BH'}, - {name: 'Bangladesh', code: 'BD'}, - {name: 'Barbados', code: 'BB'}, - {name: 'Belarus', code: 'BY'}, - {name: 'Belgium', code: 'BE'}, - {name: 'Belize', code: 'BZ'}, - {name: 'Benin', code: 'BJ'}, - {name: 'Bermuda', code: 'BM'}, - {name: 'Bhutan', code: 'BT'}, - {name: 'Bolivia', code: 'BO'}, - {name: 'Bosnia and Herzegovina', code: 'BA'}, - {name: 'Botswana', code: 'BW'}, - {name: 'Bouvet Island', code: 'BV'}, - {name: 'Brazil', code: 'BR'}, - {name: 'British Indian Ocean Territory', code: 'IO'}, - {name: 'Brunei Darussalam', code: 'BN'}, - {name: 'Bulgaria', code: 'BG'}, - {name: 'Burkina Faso', code: 'BF'}, - {name: 'Burundi', code: 'BI'}, - {name: 'Cambodia', code: 'KH'}, - {name: 'Cameroon', code: 'CM'}, - {name: 'Cape Verde', code: 'CV'}, - {name: 'Cayman Islands', code: 'KY'}, - {name: 'Central African Republic', code: 'CF'}, - {name: 'Chad', code: 'TD'}, - {name: 'Chile', code: 'CL'}, - {name: 'China', code: 'CN'}, - {name: 'Christmas Island', code: 'CX'}, - {name: 'Cocos (Keeling) Islands', code: 'CC'}, - {name: 'Colombia', code: 'CO'}, - {name: 'Comoros', code: 'KM'}, - {name: 'Congo', code: 'CG'}, - {name: 'Congo, The Democratic Republic of the', code: 'CD'}, - {name: 'Cook Islands', code: 'CK'}, - {name: 'Costa Rica', code: 'CR'}, - {name: 'Cote D\'Ivoire', code: 'CI'}, - {name: 'Croatia', code: 'HR'}, - {name: 'Cuba', code: 'CU'}, - {name: 'Cyprus', code: 'CY'}, - {name: 'Czech Republic', code: 'CZ'}, - {name: 'Denmark', code: 'DK'}, - {name: 'Djibouti', code: 'DJ'}, - {name: 'Dominica', code: 'DM'}, - {name: 'Dominican Republic', code: 'DO'}, - {name: 'Ecuador', code: 'EC'}, - {name: 'Egypt', code: 'EG'}, - {name: 'El Salvador', code: 'SV'}, - {name: 'Equatorial Guinea', code: 'GQ'}, - {name: 'Eritrea', code: 'ER'}, - {name: 'Estonia', code: 'EE'}, - {name: 'Ethiopia', code: 'ET'}, - {name: 'Falkland Islands (Malvinas)', code: 'FK'}, - {name: 'Faroe Islands', code: 'FO'}, - {name: 'Fiji', code: 'FJ'}, - {name: 'Finland', code: 'FI'}, - {name: 'French Guiana', code: 'GF'}, - {name: 'French Polynesia', code: 'PF'}, - {name: 'French Southern Territories', code: 'TF'}, - {name: 'Gabon', code: 'GA'}, - {name: 'Gambia', code: 'GM'}, - {name: 'Georgia', code: 'GE'}, - {name: 'Ghana', code: 'GH'}, - {name: 'Gibraltar', code: 'GI'}, - {name: 'Greece', code: 'GR'}, - {name: 'Greenland', code: 'GL'}, - {name: 'Grenada', code: 'GD'}, - {name: 'Guadeloupe', code: 'GP'}, - {name: 'Guam', code: 'GU'}, - {name: 'Guatemala', code: 'GT'}, - {name: 'Guernsey', code: 'GG'}, - {name: 'Guinea', code: 'GN'}, - {name: 'Guinea-Bissau', code: 'GW'}, - {name: 'Guyana', code: 'GY'}, - {name: 'Haiti', code: 'HT'}, - {name: 'Heard Island and Mcdonald Islands', code: 'HM'}, - {name: 'Holy See (Vatican City State)', code: 'VA'}, - {name: 'Honduras', code: 'HN'}, - {name: 'Hong Kong', code: 'HK'}, - {name: 'Hungary', code: 'HU'}, - {name: 'Iceland', code: 'IS'}, - {name: 'India', code: 'IN'}, - {name: 'Indonesia', code: 'ID'}, - {name: 'Iran, Islamic Republic Of', code: 'IR'}, - {name: 'Iraq', code: 'IQ'}, - {name: 'Ireland', code: 'IE'}, - {name: 'Isle of Man', code: 'IM'}, - {name: 'Israel', code: 'IL'}, - {name: 'Italy', code: 'IT'}, - {name: 'Jamaica', code: 'JM'}, - {name: 'Japan', code: 'JP'}, - {name: 'Jersey', code: 'JE'}, - {name: 'Jordan', code: 'JO'}, - {name: 'Kazakhstan', code: 'KZ'}, - {name: 'Kenya', code: 'KE'}, - {name: 'Kiribati', code: 'KI'}, - {name: 'Korea, Democratic People\'S Republic of', code: 'KP'}, - {name: 'Korea, Republic of', code: 'KR'}, - {name: 'Kuwait', code: 'KW'}, - {name: 'Kyrgyzstan', code: 'KG'}, - {name: 'Lao People\'S Democratic Republic', code: 'LA'}, - {name: 'Latvia', code: 'LV'}, - {name: 'Lebanon', code: 'LB'}, - {name: 'Lesotho', code: 'LS'}, - {name: 'Liberia', code: 'LR'}, - {name: 'Libyan Arab Jamahiriya', code: 'LY'}, - {name: 'Liechtenstein', code: 'LI'}, - {name: 'Lithuania', code: 'LT'}, - {name: 'Luxembourg', code: 'LU'}, - {name: 'Macao', code: 'MO'}, - {name: 'Macedonia, The Former Yugoslav Republic of', code: 'MK'}, - {name: 'Madagascar', code: 'MG'}, - {name: 'Malawi', code: 'MW'}, - {name: 'Malaysia', code: 'MY'}, - {name: 'Maldives', code: 'MV'}, - {name: 'Mali', code: 'ML'}, - {name: 'Malta', code: 'MT'}, - {name: 'Marshall Islands', code: 'MH'}, - {name: 'Martinique', code: 'MQ'}, - {name: 'Mauritania', code: 'MR'}, - {name: 'Mauritius', code: 'MU'}, - {name: 'Mayotte', code: 'YT'}, - {name: 'Mexico', code: 'MX'}, - {name: 'Micronesia, Federated States of', code: 'FM'}, - {name: 'Moldova, Republic of', code: 'MD'}, - {name: 'Monaco', code: 'MC'}, - {name: 'Mongolia', code: 'MN'}, - {name: 'Montenegro', code: 'ME'}, - {name: 'Montserrat', code: 'MS'}, - {name: 'Morocco', code: 'MA'}, - {name: 'Mozambique', code: 'MZ'}, - {name: 'Myanmar', code: 'MM'}, - {name: 'Namibia', code: 'NA'}, - {name: 'Nauru', code: 'NR'}, - {name: 'Nepal', code: 'NP'}, - {name: 'Netherlands', code: 'NL'}, - {name: 'Netherlands Antilles', code: 'AN'}, - {name: 'New Caledonia', code: 'NC'}, - {name: 'New Zealand', code: 'NZ'}, - {name: 'Nicaragua', code: 'NI'}, - {name: 'Niger', code: 'NE'}, - {name: 'Nigeria', code: 'NG'}, - {name: 'Niue', code: 'NU'}, - {name: 'Norfolk Island', code: 'NF'}, - {name: 'Northern Mariana Islands', code: 'MP'}, - {name: 'Norway', code: 'NO'}, - {name: 'Oman', code: 'OM'}, - {name: 'Pakistan', code: 'PK'}, - {name: 'Palau', code: 'PW'}, - {name: 'Palestinian Territory, Occupied', code: 'PS'}, - {name: 'Panama', code: 'PA'}, - {name: 'Papua New Guinea', code: 'PG'}, - {name: 'Paraguay', code: 'PY'}, - {name: 'Peru', code: 'PE'}, - {name: 'Philippines', code: 'PH'}, - {name: 'Pitcairn', code: 'PN'}, - {name: 'Poland', code: 'PL'}, - {name: 'Portugal', code: 'PT'}, - {name: 'Puerto Rico', code: 'PR'}, - {name: 'Qatar', code: 'QA'}, - {name: 'Reunion', code: 'RE'}, - {name: 'Romania', code: 'RO'}, - {name: 'Russian Federation', code: 'RU'}, - {name: 'RWANDA', code: 'RW'}, - {name: 'Saint Helena', code: 'SH'}, - {name: 'Saint Kitts and Nevis', code: 'KN'}, - {name: 'Saint Lucia', code: 'LC'}, - {name: 'Saint Pierre and Miquelon', code: 'PM'}, - {name: 'Saint Vincent and the Grenadines', code: 'VC'}, - {name: 'Samoa', code: 'WS'}, - {name: 'San Marino', code: 'SM'}, - {name: 'Sao Tome and Principe', code: 'ST'}, - {name: 'Saudi Arabia', code: 'SA'}, - {name: 'Senegal', code: 'SN'}, - {name: 'Serbia', code: 'RS'}, - {name: 'Seychelles', code: 'SC'}, - {name: 'Sierra Leone', code: 'SL'}, - {name: 'Singapore', code: 'SG'}, - {name: 'Slovakia', code: 'SK'}, - {name: 'Slovenia', code: 'SI'}, - {name: 'Solomon Islands', code: 'SB'}, - {name: 'Somalia', code: 'SO'}, - {name: 'South Africa', code: 'ZA'}, - {name: 'South Georgia and the South Sandwich Islands', code: 'GS'}, - {name: 'Spain', code: 'ES'}, - {name: 'Sri Lanka', code: 'LK'}, - {name: 'Sudan', code: 'SD'}, - {name: 'Suriname', code: 'SR'}, - {name: 'Svalbard and Jan Mayen', code: 'SJ'}, - {name: 'Swaziland', code: 'SZ'}, - {name: 'Sweden', code: 'SE'}, - {name: 'Switzerland', code: 'CH'}, - {name: 'Syrian Arab Republic', code: 'SY'}, - {name: 'Taiwan, Province of China', code: 'TW'}, - {name: 'Tajikistan', code: 'TJ'}, - {name: 'Tanzania, United Republic of', code: 'TZ'}, - {name: 'Thailand', code: 'TH'}, - {name: 'Timor-Leste', code: 'TL'}, - {name: 'Togo', code: 'TG'}, - {name: 'Tokelau', code: 'TK'}, - {name: 'Tonga', code: 'TO'}, - {name: 'Trinidad and Tobago', code: 'TT'}, - {name: 'Tunisia', code: 'TN'}, - {name: 'Turkey', code: 'TR'}, - {name: 'Turkmenistan', code: 'TM'}, - {name: 'Turks and Caicos Islands', code: 'TC'}, - {name: 'Tuvalu', code: 'TV'}, - {name: 'Uganda', code: 'UG'}, - {name: 'Ukraine', code: 'UA'}, - {name: 'United Arab Emirates', code: 'AE'}, - {name: 'United States Minor Outlying Islands', code: 'UM'}, - {name: 'Uruguay', code: 'UY'}, - {name: 'Uzbekistan', code: 'UZ'}, - {name: 'Vanuatu', code: 'VU'}, - {name: 'Venezuela', code: 'VE'}, - {name: 'Viet Nam', code: 'VN'}, - {name: 'Virgin Islands, British', code: 'VG'}, - {name: 'Virgin Islands, U.S.', code: 'VI'}, - {name: 'Wallis and Futuna', code: 'WF'}, - {name: 'Western Sahara', code: 'EH'}, - {name: 'Yemen', code: 'YE'}, - {name: 'Zambia', code: 'ZM'}, - {name: 'Zimbabwe', code: 'ZW'} -]; \ No newline at end of file diff --git a/src/main.js b/src/main.js index 7035db6a..1bd1e2c2 100644 --- a/src/main.js +++ b/src/main.js @@ -4,7 +4,7 @@ import './styles/popins.scss' import './styles/confirm.scss' import './styles/blockchain-lists.scss' -const Helpers = require('./util/ElectronHelpers').default; +import Helpers, {ipcRenderer} from './util/ElectronHelpers'; import VueInitializer from './vue/VueInitializer'; import {Routing} from './vue/Routing'; @@ -12,7 +12,6 @@ import {RouteNames} from './vue/Routing' import { QrcodeReader } from 'vue-qrcode-reader' -import MenuBar from './components/MenuBar.vue' import ViewBase from './components/ViewBase.vue' import Button from './components/reusable/Button.vue' import Input from './components/reusable/Input.vue' @@ -24,8 +23,10 @@ import Switcher from './components/reusable/Switcher.vue' import SearchAndFilter from './components/reusable/SearchAndFilter.vue' import AnimatedNumber from './components/reusable/AnimatedNumber.vue' import ActionBar from './components/reusable/ActionBar.vue' -import PopOutHead from './components/popouts/PopOutHead.vue' import WindowService from './services/electron/WindowService'; +import * as Actions from "@walletpack/core/store/constants"; +import WalletTalk from "./util/WalletTalk"; +import {store} from "./store/store"; // f12 to open console from anywhere. document.addEventListener("keydown", e => { @@ -40,10 +41,11 @@ document.onmousedown= e => { class Main { constructor(){ + console.log('This is desktop wrapper'); - const hash = location.hash.replace("#/", ''); + const isPopOut = location.hash.replace("#/", '') === 'popout'; - const shared = [ + const components = [ {tag:'Button', vue:Button}, {tag:'Input', vue:Input}, {tag:'Select', vue:Select}, @@ -57,29 +59,26 @@ class Main { {tag:'AnimatedNumber', vue:AnimatedNumber}, ]; - let fragments; - if(hash === 'popout') fragments = [ - {tag:'PopOutHead', vue:PopOutHead}, - ] - else { - fragments = [ - // {tag:'slider', vue:SliderComponent}, - {tag:'qr-reader', vue:QrcodeReader}, - ] - } - - const components = shared.concat(fragments); - const middleware = (to, next, store) => { - if(hash === 'popout') return next(); - if(Routing.isRestricted(to.name)) store.getters.unlocked ? next() : next({name:RouteNames.LOGIN}); - else next(); + const middleware = (to, next) => { + if(to.name === RouteNames.POP_OUT) return next(); + + if(!store.getters.unlocked && to.name !== RouteNames.LOGIN){ + return next({name:RouteNames.LOGIN}); + } + + return next(); }; Helpers.initializeCore(); - new VueInitializer(Routing.routes(), components, middleware, async (router, _store) => { + ipcRenderer.on('loaded', (e,payload) => { + console.log('loaded', payload); + store.dispatch(Actions.HOLD_SCATTER, payload); + }) + ipcRenderer.send('load'); - }); + new VueInitializer(Routing.routes(), components, middleware); + WalletTalk.setup(); } } diff --git a/src/migrations/10.0.0.js b/src/migrations/10.0.0.js deleted file mode 100644 index 6c55ea8b..00000000 --- a/src/migrations/10.0.0.js +++ /dev/null @@ -1,23 +0,0 @@ -import PluginRepository from '@walletpack/core/plugins/PluginRepository'; -import Explorer from "@walletpack/core/models/Explorer"; -import {BlockchainsArray} from "@walletpack/core/models/Blockchains"; -import KeyPairService from "@walletpack/core/services/secure/KeyPairService"; - -export const m10_0_0 = async scatter => { - - // Resetting explorers as structures have changed. - scatter.settings.explorers = PluginRepository.defaultExplorers(); - BlockchainsArray.map(({value:blockchain}) => { - scatter.settings.explorers[blockchain] = Explorer.fromRaw(scatter.settings.explorers[blockchain].raw); - }); - - // Removing display token as structure has changed. - scatter.settings.displayToken = null; - - // Removing all hardware wallets as uniques have changed. - scatter.keychain.keypairs.filter(x => x.external).map(x => { - scatter.keychain.removeKeyPair(x); - }); - - return true; -}; \ No newline at end of file diff --git a/src/migrations/10.1.0.js b/src/migrations/10.1.0.js deleted file mode 100644 index bf5f7d47..00000000 --- a/src/migrations/10.1.0.js +++ /dev/null @@ -1,24 +0,0 @@ -import PluginRepository from '@walletpack/core/plugins/PluginRepository'; -import Explorer from "@walletpack/core/models/Explorer"; -import {Blockchains, BlockchainsArray} from "@walletpack/core/models/Blockchains"; -import KeyPairService from "@walletpack/core/services/secure/KeyPairService"; - -export const m10_1_0 = async scatter => { - - // Clearing out the "ethereum.com" network. - scatter.settings.networks.map(x => { - if(x.host === 'ethereum.com'){ - const endorsed = PluginRepository.plugin(Blockchains.ETH).getEndorsedNetwork(); - x.host = endorsed.host; - x.protocol = endorsed.protocol; - x.port = endorsed.port; - x.name = endorsed.name; - } - }); - - scatter.settings.displayCurrency = 'USD'; - scatter.settings.displayToken = null; - scatter.settings.languageJson = null; - - return true; -}; \ No newline at end of file diff --git a/src/migrations/10.2.0.js b/src/migrations/10.2.0.js deleted file mode 100644 index 127d9adc..00000000 --- a/src/migrations/10.2.0.js +++ /dev/null @@ -1,29 +0,0 @@ -import PluginRepository from '@walletpack/core/plugins/PluginRepository'; -import Explorer from "@walletpack/core/models/Explorer"; -import {Blockchains, BlockchainsArray} from "@walletpack/core/models/Blockchains"; -import KeyPairService from "@walletpack/core/services/secure/KeyPairService"; -import IdGenerator from "@walletpack/core/util/IdGenerator"; - -export const m10_2_0 = async scatter => { - - //////////////////////////////////////////// - // Changing identity linking to unique ids instead of keypairs - //////////////////////////////////////////// - - scatter.keychain.identities.map(identity => { - if(!identity.hasOwnProperty('id')){ - identity.id = IdGenerator.text(24); - } - }); - - const getIdentityByKey = key => scatter.keychain.identities.find(x => x.publicKey === key); - - scatter.keychain.permissions.map(perm => { - if(perm.identity.length){ - const isKey = PluginRepository.plugin(Blockchains.EOSIO).validPublicKey(perm.identity); - if(isKey) perm.identity = getIdentityByKey(perm.identity).id; - } - }); - - return true; -}; \ No newline at end of file diff --git a/src/migrations/11.0.0.js b/src/migrations/11.0.0.js deleted file mode 100644 index 943c62da..00000000 --- a/src/migrations/11.0.0.js +++ /dev/null @@ -1,59 +0,0 @@ -import PluginRepository from '@walletpack/core/plugins/PluginRepository'; -import Explorer from "@walletpack/core/models/Explorer"; -import {blockchainName, Blockchains, BlockchainsArray} from "@walletpack/core/models/Blockchains"; -import KeyPairService from "@walletpack/core/services/secure/KeyPairService"; -import IdGenerator from "@walletpack/core/util/IdGenerator"; -import {LocationInformation} from "@walletpack/core/models/Identity"; - -export const m11_0_0 = async scatter => { - - const keypairs = scatter.keychain.keypairs.map(x => x.clone()); - scatter.keychain.keypairs = []; - - scatter.settings.language = 'english'; - - await Promise.all(keypairs.map(async keypair => { - delete keypair.keyHash; - - await KeyPairService.addPublicKey(keypair, Blockchains.BTC, true); - - let first = true; - keypair.blockchains.map(blockchain => { - const accounts = scatter.keychain.accounts.filter(x => x.keypairUnique === keypair.unique() && x.blockchain() === blockchain); - if(accounts.length){ - const clone = keypair.clone(); - if(!first) { - // Generating new ID - clone.id = IdGenerator.text(24); - // Re-linking all accounts. - accounts.map(account => account.keypairUnique = clone.id); - // Changing name - clone.name = `${blockchainName(blockchain)} copy of ${clone.name}`; - } - // Setting blockchains - clone.blockchains = [blockchain]; - scatter.keychain.keypairs.push(clone); - } - - first = false; - }); - - return true; - })) - - scatter.keychain.identities.map(identity => { - const location = identity.locations.length ? identity.locations[0] : LocationInformation.fromJson({name:`Location for ${identity.name}`}); - scatter.keychain.locations.push(location); - identity.location = location.id; - delete identity.locations; - }); - - const btcNetwork = PluginRepository.plugin(Blockchains.BTC).getEndorsedNetwork(); - if(!scatter.settings.networks.find(x => x.unique() === btcNetwork.unique())){ - scatter.settings.networks.push(btcNetwork); - } - - scatter.settings.explorers[Blockchains.BTC] = Explorer.fromRaw(PluginRepository.plugin(Blockchains.BTC).defaultExplorer()); - - return true; -}; \ No newline at end of file diff --git a/src/migrations/9.0.0.js b/src/migrations/9.0.0.js deleted file mode 100644 index 2554995c..00000000 --- a/src/migrations/9.0.0.js +++ /dev/null @@ -1,44 +0,0 @@ -import PluginRepository from '@walletpack/core/plugins/PluginRepository'; -import {Blockchains} from '@walletpack/core/models/Blockchains' -import Crypto from '@walletpack/core/util/Crypto'; -import {BlockchainsArray} from '@walletpack/core/models/Blockchains'; -import Seeder from '@walletpack/core/services/secure/Seeder'; - -export const m9_0_0 = async scatter => { - const seed = await Seeder.getSeed(); - - scatter.keychain.keypairs.map(x => { - if(x.hasOwnProperty('publicKeys') && x.publicKeys.length) return false; - - - - x.decrypt(seed); - - if(!x.external) { - x.privateKey = Crypto.privateKeyToBuffer(x.privateKey, x.blockchain); - x.keyHash = Crypto.bufferToHash(x.privateKey); - x.publicKeys = BlockchainsArray.map(b => ( - {blockchain:b.value, key:PluginRepository.plugin(b.value).privateToPublic(x.privateKey)} - )); - } else { - x.publicKeys = [{blockchain:x.blockchain, key:x.publicKey}]; - } - - const oldUnique = `${x.blockchain}:${x.publicKey.toLowerCase()}`; - delete x.publicKey; - delete x.blockchain; - - scatter.keychain.accounts.map(account => { - if(account.keypairUnique === oldUnique) { - account.keypairUnique = x.id; - } - }); - - x.encrypt(seed); - }); - - // Wiping out permissions - scatter.keychain.permissions = []; - - return true; -}; \ No newline at end of file diff --git a/src/migrations/9.2.0.js b/src/migrations/9.2.0.js deleted file mode 100644 index 5ccc490f..00000000 --- a/src/migrations/9.2.0.js +++ /dev/null @@ -1,19 +0,0 @@ -import PluginRepository from '@walletpack/core/plugins/PluginRepository'; -import {Blockchains} from '@walletpack/core/models/Blockchains' -import Crypto from '@walletpack/core/util/Crypto'; -import {BlockchainsArray} from '@walletpack/core/models/Blockchains'; - -export const m9_2_0 = async scatter => { - - await Promise.all(scatter.settings.networks.map(async network => { - if(network.blockchain === Blockchains.ETH && network.host === 'ethereum.com') - network = PluginRepository.plugin(Blockchains.ETH).getEndorsedNetwork(); - - return network; - })); - - if(!scatter.settings.networks.find(x => x.blockchain === Blockchains.TRX)) - scatter.settings.networks.push(PluginRepository.plugin(Blockchains.TRX).getEndorsedNetwork()); - - return true; -}; \ No newline at end of file diff --git a/src/migrations/9.5.0.js b/src/migrations/9.5.0.js deleted file mode 100644 index a5492c0f..00000000 --- a/src/migrations/9.5.0.js +++ /dev/null @@ -1,12 +0,0 @@ -import PluginRepository from '@walletpack/core/plugins/PluginRepository'; -import {Blockchains} from '@walletpack/core/models/Blockchains' -import Crypto from '@walletpack/core/util/Crypto'; -import {BlockchainsArray} from '@walletpack/core/models/Blockchains'; - -export const m9_5_0 = async scatter => { - - scatter.settings.networks = scatter.settings.networks.filter(x => x.blockchain !== Blockchains.TRX); - scatter.settings.networks.push(PluginRepository.plugin(Blockchains.TRX).getEndorsedNetwork()); - - return true; -}; \ No newline at end of file diff --git a/src/migrations/version.js b/src/migrations/version.js deleted file mode 100644 index 1fd7e714..00000000 --- a/src/migrations/version.js +++ /dev/null @@ -1,7 +0,0 @@ -export * from './9.0.0'; -export * from './9.2.0'; -export * from './9.5.0'; -export * from './10.0.0'; -export * from './10.1.0'; -export * from './10.2.0'; -export * from './11.0.0'; \ No newline at end of file diff --git a/src/models/hardware/LedgerWallet.js b/src/models/hardware/LedgerWallet.js index eda6ae17..bcec4384 100644 --- a/src/models/hardware/LedgerWallet.js +++ b/src/models/hardware/LedgerWallet.js @@ -11,8 +11,9 @@ import { Serialize } from 'eosjs'; const EthTx = require('ethereumjs-tx') import Eth from "@ledgerhq/hw-app-eth"; import {EXT_WALLET_TYPES} from "@walletpack/core/models/hardware/ExternalWallet"; -import StoreService from "@walletpack/core/services/utility/StoreService"; // import {encoderOptions, eosjsUtil} from '../../plugins/defaults/eos' +import {store} from "../../store/store"; + let encoderOptions, eosjsUtil; @@ -27,8 +28,8 @@ export const LEDGER_PATHS = { } const getTransport = () => { - if(!StoreService.get().state.hardware.hasOwnProperty('LEDGER')) return null; - return StoreService.get().state.hardware['LEDGER']; + if(!store.state.hardware.hasOwnProperty('LEDGER')) return null; + return store.state.hardware['LEDGER']; } export default class LedgerWallet { diff --git a/src/services/electron/SocketService.js b/src/services/electron/SocketService.js index c67c451a..e5418457 100644 --- a/src/services/electron/SocketService.js +++ b/src/services/electron/SocketService.js @@ -1,6 +1,5 @@ import {ipcRenderer, remote} from "../../util/ElectronHelpers"; -import StoreService from "@walletpack/core/services/utility/StoreService"; import * as CoreSocketService from '@walletpack/core/services/utility/SocketService'; import * as UIActions from "../../store/ui_actions"; import WebViewService from "./WebViewService"; @@ -9,7 +8,6 @@ import WebViewService from "./WebViewService"; export default class SocketService { static async initialize(){ - console.log('initializing') remote.getGlobal('appShared').QuitWatcher = () => { console.log('dced') SocketService.broadcastEvent('dced', {}); @@ -35,7 +33,6 @@ export default class SocketService { } static async emit(origin, id, path, data){ - console.log('emit', origin, path, data); return remote.getGlobal('appShared').LowLevelSocketService.emit(origin, id, path, data); } diff --git a/src/services/electron/StorageService.js b/src/services/electron/StorageService.js index e12bb5a7..ac3322b0 100644 --- a/src/services/electron/StorageService.js +++ b/src/services/electron/StorageService.js @@ -28,7 +28,6 @@ import {HISTORY_TYPES} from "@walletpack/core/models/histories/History"; import HistoricTransfer from "@walletpack/core/models/histories/HistoricTransfer"; import HistoricExchange from "@walletpack/core/models/histories/HistoricExchange"; import HistoricAction from "@walletpack/core/models/histories/HistoricAction"; -import StoreService from "@walletpack/core/services/utility/StoreService"; import Seeder from "@walletpack/core/services/secure/Seeder"; import ElectronHelpers from "../../util/ElectronHelpers"; import * as FileService from "./FileService"; @@ -108,9 +107,7 @@ export default class StorageService { abiStorage().clear(); historyStorage().clear(); translationStorage().clear(); - StoreService.get().commit(Actions.SET_SCATTER, null); - window.localStorage.removeItem('scatter'); - ipcFaF('key', null); + // TODO: Clear main process return true; } diff --git a/src/services/electron/WebViewService.js b/src/services/electron/WebViewService.js index 0504b3ad..5b571d79 100644 --- a/src/services/electron/WebViewService.js +++ b/src/services/electron/WebViewService.js @@ -2,6 +2,7 @@ let webView; export default class WebViewService { static set(_wv){ + console.log('setting wv', webView, _wv) webView = _wv; } diff --git a/src/services/electron/WindowService.js b/src/services/electron/WindowService.js index 65cd6c05..c9d458b4 100644 --- a/src/services/electron/WindowService.js +++ b/src/services/electron/WindowService.js @@ -1,115 +1,74 @@ import {Popup, PopupData, PopupDisplayTypes} from "../../models/popups/Popup"; +let electron = window.require('electron'); +const {remote, ipcRenderer} = electron; -console.log(require('../../util/ElectronHelpers')); - -import {ipcRenderer, remote} from '../../util/ElectronHelpers'; const path = window.require("path"); const url = window.require("url"); -console.log(remote); const LowLevelWindowService = remote.getGlobal('appShared').LowLevelWindowService; -import WindowMessage from '../../models/popups/WindowMessage'; -import StoreService from "@walletpack/core/services/utility/StoreService"; import AppsService from "@walletpack/core/services/apps/AppsService"; -import Recurring from '../../models/Recurring' -import Scatter from "@walletpack/core/models/Scatter"; -let pendingMessages = []; -const getPending = msg => pendingMessages.find(x => x.id === msg.id); -const addPending = msg => pendingMessages.push(msg); -const removePending = msg => pendingMessages = pendingMessages.filter(x => x.id !== msg.id); -const handlers = []; +let popouts = []; + -ipcRenderer.on('result', (event, result) => { +ipcRenderer.on('popoutResponse', (event, data) => { + if(!data) return; + const result = data.data; if(!result) return; - const pending = getPending(result.original); - if(pending) pending.resolver(result.result); + console.log('RESULT', result); - const rWin = remote.BrowserWindow.fromId(result.windowId); - rWin.webContents.send('ack', true); + const popout = popouts.find(x => x.id === result.original.id); + if(popout){ + popouts = popouts.filter(x => x.id !== result.original.id); + popout.resolver(result.result); + remote.BrowserWindow.fromId(data.windowId).close(); + } else { + console.error('no popout found') + } }); -let popouts = []; - -const sendMessage = (windowId, type, data, resolver = null) => { - const message = new WindowMessage(type, data, remote.getCurrentWindow().id, resolver); - if(resolver) addPending(message); - - remote.BrowserWindow.fromId(windowId).webContents.send(type, message); -}; - export default class WindowService { static openTools(){ remote.getCurrentWindow().openDevTools(); } - static flashWindow(){ - remote.getCurrentWindow().flashFrame(true); - } - - static sendAndWait(toWindowId, type, data = {}){ - return new Promise(resolve => { - sendMessage(toWindowId, type, data, resolve); - }) - } - - static sendResult(original, result = null){ - return new Promise(resolve => { - setTimeout(() => resolve(true), 5500); - const windowId = remote.getCurrentWindow().id; - - ipcRenderer.sendTo(original.windowId, 'result', {original, result, windowId}); - ipcRenderer.once(`ack`, () => resolve(true)) - - }) - } - - static watch(type, handler){ - ipcRenderer.on(type, (event, data) => handler(data)) - } - static openPopOut(popup){ return new Promise((resolve) => { popup = Popup.fromJson(popup); let responded = false; - - const scatter = StoreService.get().state.scatter.clone(); - scatter.keychain.keypairs.map(keypair => delete keypair.privateKey); - scatter.keychain.identities.map(identity => delete identity.privateKey); - delete scatter.keychain.avatars; - scatter.recurring = Recurring.placeholder(); - scatter.contacts = []; - const respond = result => { + responded = true; popouts = popouts.filter(x => x.id !== popup.id); - // popup.data.callback(Object.assign(popup, {result})); resolve(Object.assign(popup, {result})); }; + popup.resolver = respond; + // Rate limiting: One open pop out at a time per origin. - if(popouts.find(x => x.data.props.payload.origin === popup.data.props.payload.origin)) + if(popouts.find(x => x.data.props.payload.origin === popup.data.props.payload.origin)){ return resolve(false); + } + + // TODO: This should now be done on the web app popup.data.props.appData = AppsService.getAppData(popup.data.props.payload.origin); popouts.push(popup); const {width, height} = popup.dimensions(); - return LowLevelWindowService.openPopOut( - readyWindow => WindowService.sendAndWait(readyWindow.id, 'popup', {scatter, popup, balances:{}}).then(result => { - responded = true; - respond(result); - }), - closedWithoutAction => { if(!responded) respond(null); }, + const win = LowLevelWindowService.openPopOut( + popup, + () /* closed without action */ => { if(!responded) respond(null); }, width, height, popup.internal ); + }) } diff --git a/src/services/utility/BackupService.js b/src/services/utility/BackupService.js index b8495af0..bb7b52c2 100644 --- a/src/services/utility/BackupService.js +++ b/src/services/utility/BackupService.js @@ -1,7 +1,7 @@ import * as Actions from '@walletpack/core/store/constants'; import {BACKUP_STRATEGIES} from '@walletpack/core/models/Settings'; import StorageService from '../../services/electron/StorageService'; -import StoreService from "@walletpack/core/services/utility/StoreService"; +import {store} from "../../store/store"; const saveBackup = (filepath) => { const scatter = StorageService.getScatter(); @@ -10,7 +10,7 @@ const saveBackup = (filepath) => { const year = date.getUTCFullYear(); const salt = StorageService.getSalt(); const file = scatter + '|SLT|' + salt; - const name = `scatter__${StoreService.get().state.scatter.hash.substr(0,4)}-${StoreService.get().state.scatter.hash.slice(-4)}__${StoreService.get().state.scatter.meta.version}__${month}-${year}.json`; + const name = `scatter__${store.state.scatter.hash.substr(0,4)}-${store.state.scatter.hash.slice(-4)}__${store.state.scatter.meta.version}__${month}-${year}.json`; return StorageService.saveFile(filepath, name, file); }; @@ -18,9 +18,9 @@ const saveBackup = (filepath) => { export default class BackupService { static async setBackupStrategy(strategy){ - const scatter = StoreService.get().state.scatter.clone(); + const scatter = store.state.scatter.clone(); scatter.settings.autoBackup = strategy; - return StoreService.get().dispatch(Actions.SET_SCATTER, scatter); + return store.dispatch(Actions.SET_SCATTER, scatter); } static async createBackup(){ @@ -38,9 +38,9 @@ export default class BackupService { return null; })(); if(!location) return false; - const scatter = StoreService.get().state.scatter.clone(); + const scatter = store.state.scatter.clone(); scatter.settings.backupLocation = location; - return StoreService.get().dispatch(Actions.SET_SCATTER, scatter); + return store.dispatch(Actions.SET_SCATTER, scatter); } static async setDefaultBackupLocation(){ @@ -52,11 +52,11 @@ export default class BackupService { } static async createAutoBackup(){ - if(!StoreService.get().state.scatter || !StoreService.get().state.scatter.settings) return; - const strategy = StoreService.get().state.scatter.settings.autoBackup; + if(!store.state.scatter || !store.state.scatter.settings) return; + const strategy = store.state.scatter.settings.autoBackup; if(!strategy || !strategy.length || strategy === BACKUP_STRATEGIES.MANUAL) return; - const backupLocation = StoreService.get().state.scatter.settings.backupLocation; + const backupLocation = store.state.scatter.settings.backupLocation; if(!backupLocation || !backupLocation.length) return false; diff --git a/src/services/utility/LanguageService.js b/src/services/utility/LanguageService.js deleted file mode 100644 index 7235816c..00000000 --- a/src/services/utility/LanguageService.js +++ /dev/null @@ -1,53 +0,0 @@ -import {GET} from "@walletpack/core/services/apis/BackendApiService"; -import StoreService from "@walletpack/core/services/utility/StoreService"; -import * as UIActions from "../../store/ui_actions"; - -let checked = false; - -export default class LanguageService { - - static getLanguageNames(){ - return GET(`languages?names=1`) - .catch(err => { - return ["English"]; - }) - } - - static getLanguage(name){ - return GET(`languages?name=${name}`) - .then(res => { - if(!this.validateLanguage(res)) return; - return res; - }) - .catch(err => { - console.error('err', err); - return null; - }) - } - - static regenerateLanguage(){ - if(checked) return false; - checked = true; - if(!StoreService.get().state.language || !StoreService.get().state.language.json) return; - this.getLanguage(StoreService.get().state.scatter.settings.language.json).then(res => { - if(!res) return; - if(StoreService.get().state.language.json.raw !== JSON.stringify(res)){ - res.raw = JSON.stringify(res); - if(!this.validateLanguage(res)) return; - StoreService.get().dispatch(UIActions.SET_LANGUAGE, res); - } - - }) - } - - - // So basic checks against long methods which could expose - // data if DNS based attacks happen. This prevents any - // powerful method from being eval'ed within Scatter. - static validateLanguage(json){ - return json.methods.every(x => { - return x.body.toString().length <= 100; - }) - } - -} \ No newline at end of file diff --git a/src/services/utility/PasswordHelpers.js b/src/services/utility/PasswordHelpers.js deleted file mode 100644 index 42445e7c..00000000 --- a/src/services/utility/PasswordHelpers.js +++ /dev/null @@ -1,129 +0,0 @@ -import Mnemonic from "@walletpack/core/util/Mnemonic"; -import Seeder from "@walletpack/core/services/secure/Seeder"; -import StorageService from "../electron/StorageService"; -import AES from "aes-oop"; -import StoreService from "@walletpack/core/services/utility/StoreService"; -import * as Actions from "@walletpack/core/store/constants"; -import Scatter from "@walletpack/core/models/Scatter"; -import Hasher from "@walletpack/core/util/Hasher"; -import Locale from "@walletpack/core/models/Locale"; -import * as UIActions from "../../store/ui_actions"; -import PopupService from "./PopupService"; -import {Popup} from "../../models/popups/Popup"; -import IdGenerator from "@walletpack/core/util/IdGenerator"; - -export default class PasswordHelpers { - - static async seedPassword(password, setToState = true){ - return new Promise(async (resolve, reject) => { - try { - let seed, mnemonic; - if(password.split(' ').length >= 12) { - seed = await Mnemonic.mnemonicToSeed(password); - mnemonic = password; - } else { - const [m, s] = await Mnemonic.generateMnemonic(password); - seed = s; - mnemonic = m; - } - - if(setToState) { - console.log('setting to state', seed); - await Seeder.setSeed(seed); - } - resolve([mnemonic, seed]); - } catch(e){ - console.log('caugh', e); - resolve([null, null]); - } - }) - } - - static async verifyPassword(password = null, forceLocal = false){ - return new Promise(async resolve => { - - const testPassword = async (setToState, seed) => { - try { - let scatter = forceLocal ? StorageService.getLocalScatter() : StorageService.getScatter(); - scatter = AES.decrypt(scatter, seed); - if(setToState) await StoreService.get().commit(Actions.SET_SCATTER, scatter); - - if(!scatter.hasOwnProperty('keychain')) return resolve(false); - - scatter = Scatter.fromJson(scatter); - scatter.decrypt(seed); - if(setToState) await StoreService.get().dispatch(Actions.SET_SCATTER, scatter); - resolve(true); - } catch(e) { - console.log('e', e); - resolve(false); - } - } - - if(!password){ - await testPassword(true, await Seeder.getSeed()); - } else { - const [mnemonic, seed] = await PasswordHelpers.seedPassword(password, false); - await testPassword(false, seed); - } - - }) - } - - static async changePassword(newPassword){ - return new Promise(async resolve => { - - const oldSeed = await Seeder.getSeed(); - - // Setting a new salt every time the password is changed. - await StorageService.setSalt(Hasher.unsaltedQuickHash(IdGenerator.text(32))); - const [newMnemonic, newSeed] = await PasswordHelpers.seedPassword(newPassword, true); - - // Re-encrypting keypairs - const scatter = StoreService.get().state.scatter.clone(); - scatter.keychain.keypairs.map(keypair => { - keypair.decrypt(oldSeed); - keypair.encrypt(newSeed); - }); - scatter.keychain.identities.map(id => { - id.decrypt(oldSeed); - id.encrypt(newSeed); - }); - - await StoreService.get().dispatch(Actions.SET_SCATTER, scatter); - await StorageService.swapHistory(StoreService.get().state.history); - await StorageService.setTranslation(Locale.fromJson(StoreService.get().state.language.json)); - StoreService.get().dispatch(Actions.LOAD_HISTORY); - StoreService.get().dispatch(UIActions.LOAD_LANGUAGE); - resolve(newMnemonic); - - }) - } - - static async setPIN(pin, verify = false){ - return new Promise(resolve => { - const set = async () => { - const scatter = StoreService.get().state.scatter.clone(); - scatter.pin = pin ? Hasher.unsaltedQuickHash(pin) : null; - resolve(await StoreService.get().dispatch(Actions.SET_SCATTER, scatter)); - }; - - if(verify) PopupService.push(Popup.verifyPassword(verified => { - if(!verified) return resolve(null); - set(); - })); - - else set(); - }) - } - - static async verifyPIN(){ - if(!StoreService.get().state.scatter.pin || !StoreService.get().state.scatter.pin.length) return true; - return new Promise(resolve => { - PopupService.push(Popup.enterPIN(verified => { - resolve(verified); - })) - }) - } - -} \ No newline at end of file diff --git a/src/services/utility/PopupService.js b/src/services/utility/PopupService.js index 624f0710..8a638454 100644 --- a/src/services/utility/PopupService.js +++ b/src/services/utility/PopupService.js @@ -1,31 +1,31 @@ import * as Actions from '../../store/ui_actions' import {PopupDisplayTypes, Popup} from '../../models/popups/Popup'; import {RUNNING_TESTS, SHOW_POPUPS_AS_CONSOLE} from "@walletpack/core/util/TestingHelper"; -import StoreService from "@walletpack/core/services/utility/StoreService"; import WindowService from "../electron/WindowService"; +import {store} from "../../store/store"; let popouts = []; export default class PopupService { static remove(popup){ - StoreService.get().dispatch(Actions.RELEASE_POPUP, popup); + store.dispatch(Actions.RELEASE_POPUP, popup); } static push(popup){ // Allows showing popups as a console log for unit testing. if(RUNNING_TESTS && SHOW_POPUPS_AS_CONSOLE) return console.log(popup); - if(StoreService.get().state.popups.find(x => JSON.stringify(x.data) === JSON.stringify(popup.data))) + if(store.state.popups.find(x => JSON.stringify(x.data) === JSON.stringify(popup.data))) return false; if(popup.displayType === PopupDisplayTypes.POP_OUT) return this.openPopOut(popup); - StoreService.get().dispatch(Actions.PUSH_POPUP, popup); + store.dispatch(Actions.PUSH_POPUP, popup); if(popup.displayType === PopupDisplayTypes.SNACKBAR) - setTimeout(() => StoreService.get().dispatch(Actions.RELEASE_POPUP, popup), + setTimeout(() => store.dispatch(Actions.RELEASE_POPUP, popup), popup.data.props.timeout); } diff --git a/src/services/utility/RIDLService.js b/src/services/utility/RIDLService.js deleted file mode 100644 index 0f001be4..00000000 --- a/src/services/utility/RIDLService.js +++ /dev/null @@ -1,413 +0,0 @@ -import ridl, {FRAG_TYPES} from 'ridl'; -import Network from "@walletpack/core/models/Network"; -import murmur from 'murmurhash'; -import PluginRepository from "@walletpack/core/plugins/PluginRepository"; -import {Blockchains} from "@walletpack/core/models/Blockchains"; -import * as Actions from "@walletpack/core/store/constants"; -import NetworkService from "@walletpack/core/services/blockchain/NetworkService"; -import Keypair from "@walletpack/core/models/Keypair"; -import KeyPairService from "@walletpack/core/services/secure/KeyPairService"; -import IdGenerator from "@walletpack/core/util/IdGenerator"; -import AccountService from "@walletpack/core/services/blockchain/AccountService"; -import PopupService from "../../services/utility/PopupService"; -import {Popup} from "../../models/popups/Popup"; -import Account from "@walletpack/core/models/Account"; -import ecc from 'eosjs-ecc'; -import StoreService from "@walletpack/core/services/utility/StoreService"; - -export const RIDL_API = `https://api.ridl.network/v1`; - -const RIDL_NET_NAME = `RIDL Network`; -export let network; - -const finger = x => murmur.v2(x); - -//TODO: GET FROM API -const dangerFrags = [ - finger('scam'), - finger('dangerous'), - finger('privacy'), -]; - -let dangerFragTypes = null; -const fillFrags = async reputable => { - const fragments = reputable.reputation.fragments.filter(x => dangerFrags.includes(x.fingerprint)); - const fragTypes = !dangerFragTypes ? await ridl.reputation.getFragmentsFor(reputable) : dangerFragTypes; - - fragments.map(frag => { - const typed = fragTypes.find(x => x.fingerprint === frag.fingerprint); - frag.upTag = typed ? typed.upTag : 'good'; - frag.downTag = typed ? typed.downTag : 'bad'; - }); - - return fragments; -}; - -const fetchChainId = network => { - return fetch(`${network.fullhost()}/v1/chain/get_info`).then(x => x.json()).then(x => x.chain_id).catch(() => null); -}; - -let isConnected = false; -let checkedAccounts = false; -export default class RIDLService { - - static async init(){ - if(isConnected) return; - const n = await fetch(`${RIDL_API}/network`).then(x => x.json()).then(x => Network.fromJson(x)).catch(() => null); - if(!n) return console.error('Could not fetch RIDL Network'); - - n.name = RIDL_NET_NAME; - n.chainId = await fetchChainId(n); - network = n; - - await RIDLService.addNetwork(); - - await ridl.init(network); - isConnected = true; - return true; - } - - static networkUnique(){ - if(!network) return; - return network.unique(); - } - - static async hasNetwork(){ - if(!network) await this.init(); - return !!StoreService.get().state.scatter.settings.networks.find(x => x.chainId === network.chainId); - } - - static async addNetwork(){ - if(await this.hasNetwork()) return true; - return NetworkService.addNetwork(network, false); - } - - static async checkAccounts(){ - if(checkedAccounts) return; - checkedAccounts = true; - const n = StoreService.get().state.scatter.settings.networks.find(x => x.chainId === network.chainId); - if(!n) return console.error("No RIDL network found!"); - - const plugin = PluginRepository.plugin(Blockchains.EOSIO); - const accounts = StoreService.get().state.scatter.keychain.accounts.filter(x => x.networkUnique === n.unique()).reduce((acc, x) => { - if(acc.find(y => y.name === x.name)) return acc; - acc.push(x); - return acc; - }, []); - - await Promise.all(accounts.map(async account => { - const data = await plugin.accountData(account, network); - if(!data) await AccountService.removeAccounts([account]); - return true; - })); - - const identity = StoreService.get().state.scatter.keychain.identities[0]; - if(identity.ridl !== -1){ - const exists = await RIDLService.identityNameIsAvailable(identity.name); - if(!exists || exists.id !== identity.ridl){ - const scatter = StoreService.get().state.scatter.clone(); - scatter.keychain.identities[0].ridl = -1; - await StoreService.get().dispatch(Actions.SET_SCATTER, scatter); - } - } - - return true; - } - - static getAccount(){ - const n = StoreService.get().state.scatter.settings.networks.find(x => x.chainId === network.chainId); - if(!n) return console.error("No RIDL network found!"); - return StoreService.get().state.scatter.keychain.accounts.find(x => x.networkUnique === n.unique()); - } - - static async checkApp(app){ - if(!StoreService.get().state.scatter.settings.firewall.enabled) return; - await this.init(); - const reputable = await ridl.reputation.searchByFingerprint(FRAG_TYPES.APPLICATION, app.trim()); - if(!reputable) return; - - const fragments = await fillFrags(reputable); - - return { - decimal:reputable.decimalReputation(true, dangerFrags), - fragments, - reputable - }; - } - - static async checkContracts(contractNetwork, contracts){ - if(!StoreService.get().state.scatter.settings.firewall.enabled) return; - await this.init(); - - const networkId = `${contractNetwork.blockchain}::${contractNetwork.chainId}`; - let fragTypes = []; - - const reputables = await Promise.all(contracts.map(({code:contract, type:action}) => { - return ridl.reputation.searchByFingerprint(FRAG_TYPES.BLOCKCHAIN_ADDR, contract.toLowerCase(), networkId).then(async reputable => { - reputable.code = contract; - reputable.decimal = reputable.decimalReputation(true, dangerFrags); - reputable.children = (await this.getChildren(reputable)).filter(x => x.entity.toLowerCase() === action.toLowerCase()); - reputable.children.map(child => { - child.code = contract+action; - child.decimal = child.decimalReputation(true, dangerFrags); - }); - await fillFrags(reputable); - return reputable; - }); - })); - - let total = 0; - let actionables = []; - reputables.map(reputable => { - if(reputable.children.length){ - reputable.children.map(child => { - total += parseFloat(child.decimal); - actionables.push(child); - }) - } - else { - total += parseFloat(reputable.decimal); - actionables.push(reputable); - } - }); - - return { - decimal:parseFloat(total).toFixed(1), - reputables:actionables, - } - - } - - static async getChildren(reputable){ - await ridl.canConnect() - return ridl.reputation.searchByParent(reputable.id); - } - - static isValidName(name){ - return ridl.identity.validName(name); - } - - static async identityNameIsAvailable(name){ - return ridl.identity.get(name); - } - - static async setSignatureProvider(reject){ - const account = this.getAccount(); - const plugin = PluginRepository.plugin(Blockchains.EOSIO); - const provider = () => payload => { - return plugin.signerWithPopup(payload, account, reject); - }; - await ridl.init( network, account, provider() ); - return true; - } - - static async identify(username, publicKey){ - return new Promise(async (resolve, reject) => { - await this.setSignatureProvider(reject); - const identified = await ridl.identity.payAndIdentify(username, publicKey).then(() => true).catch(() => false); - setTimeout(async () => { - const identity = await ridl.identity.get(username); - if(!identity) return resolve(false); - resolve(identity); - }, 1000); - }) - } - - static async changeAccount(username, newAccountName){ - return new Promise(async (resolve, reject) => { - await this.setSignatureProvider(reject); - const identified = await ridl.identity.changeacc(username, newAccountName).then(() => true).catch(() => false); - setTimeout(async () => { - const identity = await ridl.identity.get(username); - if(!identity) return resolve(false); - resolve(identity); - }, 1000); - }) - } - - static async claim(username, publicKey){ - return new Promise(async (resolve, reject) => { - await this.setSignatureProvider(reject); - const plugin = PluginRepository.plugin(Blockchains.EOSIO); - const signature = await plugin.signer({data:ecc.sha256('ridl')}, publicKey, true, true); - const identified = await ridl.identity.claim(username, publicKey, signature).then(() => true).catch(() => false); - resolve(identified); - }) - } - - static async changeKey(username, publicKey){ - return new Promise(async (resolve, reject) => { - await this.setSignatureProvider(reject); - const changed = await ridl.identity.changekey(username, publicKey).then(() => true).catch(() => false); - resolve(changed); - }) - } - - static async createAccount(){ - await this.addNetwork(); - const ridlNetwork = StoreService.get().state.scatter.settings.networks.find(x => x.chainId === network.chainId); - - let keypair = StoreService.get().state.scatter.keychain.keypairs.find(x => x.name.indexOf('RIDL Key') > -1); - if(!keypair){ - keypair = Keypair.placeholder(); - keypair.blockchains = [Blockchains.EOSIO]; - await KeyPairService.generateKeyPair(keypair); - await KeyPairService.makePublicKeys(keypair); - keypair.name = `RIDL Key-${IdGenerator.text(5)}`; - await KeyPairService.saveKeyPair(keypair); - } - - const publicKey = keypair.publicKeys.find(x => x.blockchain === Blockchains.EOSIO).key; - const created = await fetch(`${RIDL_API}/create/${publicKey}`).then(x => x.json()); - - if(!created){ - PopupService.push(Popup.snackbar('Could not create account on the RIDL network. Please try again soon.')) - return false; - } - - const account = Account.fromJson(created); - account.networkUnique = ridlNetwork.unique(); - account.keypairUnique = keypair.unique(); - await AccountService.addAccount(account); - - return account; - } - - static getRidlCycle(){ - const started = 1531720800; - return Math.floor(((+new Date()/1000 - started) / 3600) / 12); - } - - static async getRidlContributions(){ - const plugin = PluginRepository.plugin(Blockchains.EOSIO); - const network = StoreService.get().state.scatter.settings.networks.find(x => x.chainId === plugin.getEndorsedNetwork().chainId); - - const accounts = StoreService.get().state.scatter.keychain.accounts.filter(x => x.networkUnique === network.unique()).reduce((arr, account) => { - if(arr.find(x => x.name === account.name)) return arr; - arr.push(account); - return arr; - }, []); - - return (await Promise.all(accounts.map(account => { - return fetch(`${network.fullhost()}/v1/chain/get_table_rows`, { - method:"POST", - body:JSON.stringify({ - json:true, - code:'scatterfunds', - scope:account.name, - table:'claimables' - }) - }).then(x => x.json()).then(x => { - return { - account:account.name, - rows:x.rows, - }; - }).catch(error => { - return { - account:account.name, - rows:[], - }; - }) - }))).filter(x => x.rows.length) - - - } - - static async getCycleData(){ - - const plugin = PluginRepository.plugin(Blockchains.EOSIO); - const network = StoreService.get().state.scatter.settings.networks.find(x => x.chainId === plugin.getEndorsedNetwork().chainId); - - const cycle = this.getRidlCycle(); - - return fetch(`${network.fullhost()}/v1/chain/get_table_rows`, { - method:"POST", - body:JSON.stringify({ - json:true, - code:'scatterfunds', - scope:cycle, - table:'cycles' - }) - }).then(x => x.json()).then(x => { - if(!x.rows.length) return {cycle, tokens:'0.0000 EOS'}; - return x.rows[0]; - }).catch(error => { - return {cycle, tokens:'0.0000 EOS'}; - }) - } - - static async donateToScatter(account, quantity){ - - quantity = parseFloat(quantity.split(' ')[0]).toFixed(4) + ' EOS'; - - return new Promise(async (resolve, reject) => { - const plugin = PluginRepository.plugin(Blockchains.EOSIO); - const eos = plugin.getSignableEosjs(account, reject); - - await eos.transact({ - actions:[{ - account: 'eosio.token', - name:'transfer', - authorization: [{ - actor: account.sendable(), - permission: account.authority, - }], - data:{ - from:account.name, - to:'scatterfunds', - quantity, - memo:'Donated to Scatter' - }, - }] - }, { - blocksBehind: 3, - expireSeconds: 30, - }) - .then(trx => { - PopupService.push(Popup.transactionSuccess(Blockchains.EOSIO, trx.transaction_id)); - resolve(trx); - }) - .catch(res => { - console.error(error); - reject(null); - }) - }) - } - - static async claimRidlTokens(accounts){ - return new Promise(async (resolve, reject) => { - const plugin = PluginRepository.plugin(Blockchains.EOSIO); - const eos = plugin.getSignableEosjs(accounts, reject); - - const actions = accounts.map(account => { - return { - account: 'scatterfunds', - name:'claim', - authorization: [{ - actor: account.sendable(), - permission: account.authority, - }], - data:{ - owner:account.name - }, - } - }); - - await eos.transact({ actions }, { blocksBehind: 3, expireSeconds: 30, }) - .then(res => { - PopupService.push(Popup.transactionSuccess(Blockchains.EOSIO, res.transaction_id)); - resolve(res); - }).catch(err => reject(err)); - }) - } - - static async getLastKnownBlock(){ - if(!network) return null; - const headBlock = await fetch(`${network.fullhost()}/v1/chain/get_info`).then(x => x.json()).then(x => x.head_block_num).catch(() => null); - const clone = StoreService.get().state.scatter.clone(); - clone.settings.firewall.lastKnownBlock = headBlock; - return StoreService.get().dispatch(Actions.SET_SCATTER, clone); - } - - -} \ No newline at end of file diff --git a/src/services/utility/RecurringService.js b/src/services/utility/RecurringService.js deleted file mode 100644 index 1cfe15a7..00000000 --- a/src/services/utility/RecurringService.js +++ /dev/null @@ -1,70 +0,0 @@ -import * as Actions from "@walletpack/core/store/constants"; -import PluginRepository from "@walletpack/core/plugins/PluginRepository"; -import {Blockchains} from "@walletpack/core/models/Blockchains"; -import StoreService from "@walletpack/core/services/utility/StoreService"; - -let checkedProxies = false; -export default class RecurringService { - - static async addProxy(account, proxy){ - const scatter = StoreService.get().state.scatter.clone(); - scatter.recurring.proxies = scatter.recurring.proxies.filter(x => x.account !== account.identifiable()); - scatter.recurring.proxies.push({ - account:account.identifiable(), - proxy, - timestamp:+new Date() - }); - return StoreService.get().dispatch(Actions.SET_SCATTER, scatter); - } - - static async removeProxies(accounts){ - const scatter = StoreService.get().state.scatter.clone(); - const identifiables = accounts.map(x => x.identifiable()); - scatter.recurring.proxies = scatter.recurring.proxies.filter(x => !identifiables.includes(x.account)); - return StoreService.get().dispatch(Actions.SET_SCATTER, scatter); - } - - static async touchProxies(accounts){ - const scatter = StoreService.get().state.scatter.clone(); - const identifiables = accounts.map(x => x.identifiable()); - scatter.recurring.proxies.filter(x => identifiables.includes(x.account)).map(x => x.timestamp = +new Date()); - return StoreService.get().dispatch(Actions.SET_SCATTER, scatter); - } - - static async checkProxies(){ - if(checkedProxies) return; - checkedProxies = true; - - const scatter = StoreService.get().state.scatter.clone(); - const accounts = StoreService.get().state.scatter.keychain.accounts; - - const accountIds = accounts.map(x => x.identifiable()); - const removedAccounts = scatter.recurring.proxies.filter(x => !accountIds.includes(x.account)); - if(removedAccounts.length) await this.removeProxies(removedAccounts); - - const now = +new Date(); - const staleTime = 1000*60*60*24*7; // 7 days - const staleProxies = scatter.recurring.proxies.filter(proxy => { - return now > proxy.timestamp + staleTime; - }); - - - if(!staleProxies) return true; - - const proxied = (await Promise.all(staleProxies.map(async proxy => { - const account = accounts.find(x => x.identifiable() === proxy.account); - return PluginRepository.plugin(account.blockchain()).proxyVote(account, proxy.proxy).then(res => { - return {res, account} - }); - }))); - - const failed = proxied.filter(x => !x.res); - const succeeded = proxied.filter(x => x.res); - - if(failed.length) await this.removeProxies(failed.map(x => x.account)); - if(succeeded.length) await this.touchProxies(succeeded.map(x => x.account)); - - return true; - } - -} \ No newline at end of file diff --git a/src/services/utility/SingletonService.js b/src/services/utility/SingletonService.js deleted file mode 100644 index 93ba12e8..00000000 --- a/src/services/utility/SingletonService.js +++ /dev/null @@ -1,26 +0,0 @@ - -import AccountService from "@walletpack/core/services/blockchain/AccountService"; -import PriceService from "@walletpack/core/services/apis/PriceService"; -import PermissionService from "@walletpack/core/services/apps/PermissionService"; -import StoreService from "@walletpack/core/services/utility/StoreService"; -import SocketService from "@walletpack/core/services/utility/SocketService"; -import AppsService from "@walletpack/core/services/apps/AppsService"; -import PluginRepository from "@walletpack/core/plugins/PluginRepository"; -import { Blockchains } from "@walletpack/core/models/Blockchains"; - -let initialized = false; -export default class SingletonService { - static async init() { - if (initialized) return true; - initialized = true; - // TODO: - // PluginRepository.plugin(Blockchains.TRX).init(); - SocketService.initialize(); - AppsService.getApps(); - PriceService.watchPrices(); - PermissionService.removeDanglingPermissions(); - AccountService.fixOrphanedAccounts(); - return true; - } - -} \ No newline at end of file diff --git a/src/services/utility/UpdateService.js b/src/services/utility/UpdateService.js deleted file mode 100644 index 1083a207..00000000 --- a/src/services/utility/UpdateService.js +++ /dev/null @@ -1,46 +0,0 @@ -import {mathematicalVersion} from '@walletpack/core/migrations/migrator'; -import PopupService from './PopupService'; -import {Popup} from '../../models/popups/Popup' -import StoreService from "@walletpack/core/services/utility/StoreService"; -import {GET} from "@walletpack/core/services/apis/BackendApiService"; - -export default class UpdateService { - - static updateUrl(){ return `https://github.com/GetScatter/ScatterDesktop/releases` } - - static async needsUpdate(){ - const scatter = StoreService.get().state.scatter.clone(); - const update = await this.needsUpdateNoPrompt(); - - if(update){ - const {version, body, name} = update; - - // Not setting last version anymore. - // We want to always pop it up to annoy users - // whenever they open Scatter so that they update - // since there's often security updates. - // -------------------------------------- - // scatter.meta.lastSuggestedVersion = version; - // StoreService.get().dispatch(Actions.SET_SCATTER, scatter); - - PopupService.push(Popup.updateAvailable(update, updated => { - - })); - } - } - - static async needsUpdateNoPrompt(useLastVersion = true){ - - // Always getting update from backend - const {version, details, name} = await GET(`version`).catch(() => {}); - - const scatter = StoreService.get().state.scatter.clone(); - let lastSuggested = scatter.meta.lastSuggestedVersion; - - if(mathematicalVersion(scatter.meta.version) < version && (!useLastVersion || (!lastSuggested || lastSuggested !== version))) - return {version, url:this.updateUrl(), name, body:details}; - - return false; - } - -} \ No newline at end of file diff --git a/src/store/actions.js b/src/store/actions.js index 2b24f9bd..e2d6317b 100644 --- a/src/store/actions.js +++ b/src/store/actions.js @@ -4,83 +4,30 @@ import BackupService from '../services/utility/BackupService'; import Hasher from '@walletpack/core/util/Hasher' import IdGenerator from '@walletpack/core/util/IdGenerator' import Scatter from '@walletpack/core/models/Scatter'; -import AES from 'aes-oop'; -import {RUNNING_TESTS} from "@walletpack/core/util/TestingHelper"; -import Seeder from "@walletpack/core/services/secure/Seeder"; import * as UIActions from "./ui_actions"; -import PasswordHelpers from "../services/utility/PasswordHelpers"; +const {Wallet, Storage} = window.require('electron').remote.getGlobal('appShared'); export const actions = { - [UIActions.SET_PORTS]:({commit}, x) => commit(UIActions.SET_PORTS, x), - [UIActions.SET_SIDEBAR]:({commit}, x) => commit(UIActions.SET_SIDEBAR, x), - [UIActions.SET_APP_REP]:({commit}, x) => commit(UIActions.SET_APP_REP, x), - [UIActions.SET_ACTION_REP]:({commit}, x) => commit(UIActions.SET_ACTION_REP, x), - [Actions.SET_PRICE_DATA]:({commit}, x) => commit(Actions.SET_PRICE_DATA, x), - [UIActions.HIDE_BACK_BTN]:({commit}, x) => commit(UIActions.HIDE_BACK_BTN, x), - [Actions.ADD_RESOURCES]:({commit}, x) => commit(Actions.ADD_RESOURCES, x), - [Actions.SET_RESOURCES]:({commit}, x) => commit(Actions.SET_RESOURCES, x), - [UIActions.SET_PROCESS]:({commit}, x) => commit(UIActions.SET_PROCESS, x), - [UIActions.RELEASE_PROCESS]:({commit}, x) => commit(UIActions.RELEASE_PROCESS, x), - [UIActions.SET_WORKING_SCREEN]:({commit}, x) => commit(UIActions.SET_WORKING_SCREEN, x), - [Actions.SET_DAPP_DATA]:({commit}, x) => commit(Actions.SET_DAPP_DATA, x), - [Actions.SET_DAPP_LOGO]:({commit}, x) => commit(Actions.SET_DAPP_LOGO, x), - [UIActions.SET_SEARCH_TERMS]:({commit}, terms) => commit(UIActions.SET_SEARCH_TERMS, terms), [Actions.HOLD_SCATTER]:({commit}, scatter) => commit(Actions.SET_SCATTER, scatter), - - [UIActions.SET_SEED]:({commit}, password) => { - return new Promise(async (resolve, reject) => { - console.log('seeting') - const [mnemonic, seed] = await PasswordHelpers.seedPassword(password, true); - console.log('mne', mnemonic, seed); - resolve(mnemonic); - }) - }, - - [Actions.LOAD_SCATTER]:async ({commit, state, dispatch}, forceLocal = false) => { - - if(!state.scatter) { - let scatter = StorageService.getScatter(); - if (!scatter) return null; - return commit(Actions.SET_SCATTER, scatter); - } - - if(await PasswordHelpers.verifyPassword(null, forceLocal)){ - const scatter = state.scatter.clone(); - - if(!RUNNING_TESTS){ - await require('@walletpack/core/migrations/migrator').default(scatter, require('../migrations/version')); - } - - scatter.meta.regenerateVersion(); - await dispatch(Actions.SET_SCATTER, scatter); - } - - return true; - }, - [UIActions.CREATE_SCATTER]:({state, commit, dispatch}, password) => { return new Promise(async (resolve, reject) => { const scatter = await Scatter.create(); scatter.meta.acceptedTerms = true; - await StorageService.setSalt(Hasher.unsaltedQuickHash(IdGenerator.text(32))); + await Storage.setSalt(Hasher.unsaltedQuickHash(IdGenerator.text(32))); - dispatch(UIActions.SET_SEED, password).then(mnemonic => { - dispatch(Actions.SET_SCATTER, scatter).then(async _scatter => { - await BackupService.setDefaultBackupLocation(); - resolve(); - }) - }) + await Wallet.unlock(password); + dispatch(Actions.SET_SCATTER, scatter).then(async _scatter => { + await BackupService.setDefaultBackupLocation(); + resolve(); + }) }) }, [Actions.SET_SCATTER]:async ({commit, state}, scatter) => { return new Promise(async resolve => { - const seed = await Seeder.getSeed(); - const savable = AES.encrypt(scatter.savable(seed), seed); - StorageService.setLocalScatter(savable); - StorageService.setScatter(savable).then(() => BackupService.createAutoBackup()); + await Wallet.updateScatter(scatter); commit(Actions.SET_SCATTER, scatter); resolve(scatter); }) @@ -90,25 +37,4 @@ export const actions = { [UIActions.RELEASE_POPUP]:({commit}, popup) => commit(UIActions.RELEASE_POPUP, popup), [UIActions.SET_HARDWARE]:({commit}, hardware) => commit(UIActions.SET_HARDWARE, hardware), [UIActions.REMOVE_HARDWARE]:({commit}, key) => commit(UIActions.REMOVE_HARDWARE, key), - [UIActions.SET_TOKENS]:({commit}, tokens) => commit(UIActions.SET_TOKENS, tokens), - [Actions.SET_BALANCES]:({commit}, x) => commit(Actions.SET_BALANCES, x), - [UIActions.SET_FULL_BALANCES]:({commit}, x) => commit(UIActions.SET_FULL_BALANCES, x), - [Actions.REMOVE_BALANCES]:({commit}, x) => commit(Actions.REMOVE_BALANCES, x), - [Actions.SET_PRICES]:({commit}, prices) => commit(Actions.SET_PRICES, prices), - [UIActions.NEW_KEY]:({commit}, x) => commit(UIActions.NEW_KEY, x), - [UIActions.SET_LANGUAGE]:({commit}, x) => { - commit(UIActions.SET_LANGUAGE, x); - return StorageService.setTranslation(x); - }, - [UIActions.LOAD_LANGUAGE]:async ({commit}) => commit(UIActions.SET_LANGUAGE, await StorageService.getTranslation()), - [Actions.LOAD_HISTORY]:async ({commit}) => commit(Actions.LOAD_HISTORY, await StorageService.getHistory()), - [Actions.UPDATE_HISTORY]:async ({commit}, x) => { - await StorageService.updateHistory(x); - commit(Actions.LOAD_HISTORY, await StorageService.getHistory()) - }, - [Actions.DELTA_HISTORY]:({commit}, x) => { - commit(Actions.DELTA_HISTORY, x); - return StorageService.deltaHistory(x); - }, - }; diff --git a/src/store/mutations.js b/src/store/mutations.js index 1bd35d50..24a32f47 100644 --- a/src/store/mutations.js +++ b/src/store/mutations.js @@ -3,43 +3,10 @@ import Vue from 'vue'; import * as UIActions from "./ui_actions"; export const mutations = { - [UIActions.SET_PORTS]:(state, x) => state.ports = x, - [UIActions.SET_SIDEBAR]:(state, x) => state.sidebarLocked = x, - [UIActions.SET_APP_REP]:(state, x) => state.appReputation = x, - [UIActions.SET_ACTION_REP]:(state, {app, rep}) => Vue.set(state.actionReputations, app, rep), - [Mutations.SET_PRICE_DATA]:(state, x) => state.priceData = x, - [UIActions.HIDE_BACK_BTN]:(state, x) => state.hideBackButton = x, - [UIActions.SET_WORKING_SCREEN]:(state, x) => state.workingScreen = x, - [UIActions.SET_SEARCH_TERMS]:(state, terms) => state.searchTerms = terms, // [Mutations.SET_MNEMONIC]:(state, mnemonic) => state.mnemonic = mnemonic, [Mutations.SET_SCATTER]:(state, scatter) => state.scatter = scatter, [UIActions.PUSH_POPUP]:(state, popup) => state.popups.push(popup), [UIActions.RELEASE_POPUP]:(state, popup) => state.popups = state.popups.filter(p => p.id !== popup.id), - [UIActions.SET_TOKENS]:(state, tokens) => state.tokens = tokens, - [Mutations.SET_PRICES]:(state, prices) => state.prices = prices, - [Mutations.SET_DAPP_LOGO]:(state, {origin, logo}) => Vue.set(state.dappLogos, origin, logo), - [Mutations.SET_DAPP_DATA]:(state, data) => state.dappData = data, - [UIActions.RELEASE_PROCESS]:(state, p) => state.processes = state.processes.filter(x => x.id !== p.id), - [UIActions.SET_PROCESS]:(state, p) => { - const process = state.processes.find(x => x.id === p.id); - if(!process) return state.processes.push(p); - else process.progress = p.progress; - }, - [Mutations.SET_RESOURCES]:(state, x) => state.resources = x, - [Mutations.ADD_RESOURCES]:(state, x) => Vue.set(state.resources, x.acc, x.res), - [Mutations.SET_BALANCES]:(state, x) => Vue.set(state.balances, x.account, x.balances), - [UIActions.SET_FULL_BALANCES]:(state, x) => state.balances = x, - [Mutations.REMOVE_BALANCES]:(state, accountKeys) => accountKeys.map(key => Vue.delete(state.balances, key)), [UIActions.SET_HARDWARE]:(state, hardware) => Vue.set(state.hardware, hardware.name, hardware.transport), [UIActions.REMOVE_HARDWARE]:(state, key) => Vue.delete(state.hardware, key), - [UIActions.NEW_KEY]:(state, x) => state.newKey = x, - [UIActions.SET_LANGUAGE]:(state, x) => Vue.set(state.language, 'json', x), - [Mutations.LOAD_HISTORY]:(state, x) => state.history = x, - [Mutations.DELTA_HISTORY]:(state, x) => { - if(x === null) state.history = []; - else { - if(state.history.find(h => h.id === x.id)) state.history = state.history.filter(h => h.id !== x.id); - else state.history.unshift(x); - } - }, }; \ No newline at end of file diff --git a/src/store/store.js b/src/store/store.js index 1ea554fe..f75a71b2 100644 --- a/src/store/store.js +++ b/src/store/store.js @@ -12,84 +12,23 @@ import BalanceService from "@walletpack/core/services/blockchain/BalanceService" Vue.use(Vuex); export const state = { - ports:null, - dappLogos:{}, - dappData:{}, workingScreen:null, - processes:[], - resources:{}, - hideBackButton:false, - - searchTerms:'', - mnemonic:'', - scatter:null, - popups:[], - hardware:{}, - - balances:{}, - prices:{}, - - newKey:false, - - history:[], - language:{}, - - priceData:{}, - - appReputation:false, - actionReputations:{}, - - sidebarLocked:false, }; export const getters = { // App State unlocked:state => state.scatter !== null && typeof state.scatter !== 'string' - && typeof state.scatter.isEncrypted === 'function' - && !state.scatter.isEncrypted(), - - - contacts:state => state.scatter.contacts || [], - - // Keychain centric - identity:state => state.scatter.keychain.identities[0], - identities:state => state.scatter.keychain.identities || [], - avatars:state => state.scatter.keychain.avatars || {}, - locations:state => state.scatter.keychain.locations || [], - keypairs:state => state.scatter.keychain.keypairs || [], - cards:state => state.scatter.keychain.cards || [], - accounts:state => state.scatter.keychain.accounts || [], - permissions:state => state.scatter.keychain.permissions || [], - apps:state => state.scatter.keychain.apps || [], - - // Settings - hideMainBalance:state => state.scatter.settings.hideMainBalance, - ridlEnabled:state => state.scatter.settings.firewall.enabled, - version:state => state.scatter.meta.version, - networks:state => state.scatter.settings.networks || [], - language:state => Locale.fromJson(state.language.json), - autoBackup:state => state.scatter.settings.autoBackup || null, - backupLocation:state => state.scatter.settings.backupLocation || null, - explorers:state => state.scatter.settings.explorers || PluginRepository.defaultExplorers(), - blacklistActions:state => state.scatter.settings.blacklistActions, - blacklistTokens:state => state.scatter.settings.blacklistTokens, - balanceFilters:state => state.scatter.settings.balanceFilters, - displayCurrency:state => state.scatter.settings.displayCurrency, - displayToken:state => state.scatter.settings.displayToken, - tokens:state => state.scatter.settings.tokens, + && typeof state.scatter.keychain !== 'string', // Popups popIns:state => state.popups.filter(x => x.displayType === PopupDisplayTypes.POP_IN) || [], nextPopIn:state => state.popups.filter(x => x.displayType === PopupDisplayTypes.POP_IN)[0] || null, snackbars:state => state.popups.filter(x => x.displayType === PopupDisplayTypes.SNACKBAR) || [], - totalBalances:(state, getters) => { - return BalanceService.totalBalances(false); - }, }; export const store = new Vuex.Store({ diff --git a/src/store/ui_actions.js b/src/store/ui_actions.js index 729b861e..6b4509e7 100644 --- a/src/store/ui_actions.js +++ b/src/store/ui_actions.js @@ -1,24 +1,8 @@ export const PUSH_POPUP = 'pushPopup'; export const RELEASE_POPUP = 'releasePopup'; -export const SET_WORKING_SCREEN = 'setWorkingScreen'; export const CREATE_SCATTER = 'createScatter'; -export const LOAD_LANGUAGE = 'loadLanguage'; -export const SET_LANGUAGE = 'setLanguage'; -export const SET_APP_REP = 'setAppRep'; - -export const SET_SIDEBAR = 'setSidebar'; -export const SET_PORTS = 'setPorts'; - -export const SET_ACTION_REP = 'SET_ACTION_REP'; -export const HIDE_BACK_BTN = 'HIDE_BACK_BTN'; -export const SET_PROCESS = 'SET_PROCESS'; -export const RELEASE_PROCESS = 'RELEASE_PROCESS'; -export const SET_SEARCH_TERMS = 'SET_SEARCH_TERMS'; export const SET_HARDWARE = 'SET_HARDWARE'; export const REMOVE_HARDWARE = 'REMOVE_HARDWARE'; -export const SET_TOKENS = 'SET_TOKENS'; -export const SET_FULL_BALANCES = 'SET_FULL_BALANCES'; -export const NEW_KEY = 'NEW_KEY'; export const SET_SEED = 'SET_SEED'; \ No newline at end of file diff --git a/src/util/ElectronHelpers.js b/src/util/ElectronHelpers.js index 7ac11ea8..db76e495 100644 --- a/src/util/ElectronHelpers.js +++ b/src/util/ElectronHelpers.js @@ -80,7 +80,8 @@ export default class ElectronHelpers { if(type === 'popout') { console.log('POPOUT!') const popup = new Popup(PopupDisplayTypes.POP_OUT, new PopupData(data.type, data)); - return WindowService.openPopOut(popup); + const popoutResult = await WindowService.openPopOut(popup); + console.log("POPOUT RESULT?", popoutResult); } }; @@ -88,11 +89,16 @@ export default class ElectronHelpers { ScatterCore.initialize( { - blockchains:{}, + blockchains:{ + EOSIO:'eos', + ETH:'eth', + TRX:'trx', + BTC:'btc', + }, plugins:[ require('@walletpack/eosio').default, require('@walletpack/ethereum').default, - // require('@walletpack/tron').default, + require('@walletpack/tron').default, require('@walletpack/bitcoin').default, ] }, diff --git a/src/util/WalletTalk.js b/src/util/WalletTalk.js new file mode 100644 index 00000000..a4f42e02 --- /dev/null +++ b/src/util/WalletTalk.js @@ -0,0 +1,53 @@ + +const electron = window.require('electron'); +const {ipcRenderer} = electron; + +import WebViewService from "../services/electron/WebViewService"; +import Scatter from "@walletpack/core/models/Scatter"; +import * as Actions from "@walletpack/core/store/constants"; +import {store} from "../store/store"; + + +const services = { + FileService:require('../services/electron/FileService'), + SocketService:require('../services/electron/SocketService').default, + StorageService:require('../services/electron/StorageService').default, + WindowService:require('../services/electron/WindowService').default, +} + +export default class WalletTalk { + + static setup(){ + ipcRenderer.on('popoutResponse', async (e, payload) => { + console.log('popoutresponse', payload); + WebViewService.get().send('scatter', {data:payload.result, id:payload.original.id}) + }); + + ipcRenderer.on('scatter', async (e, payload) => { + const {service, method, data, id, isPopOut} = payload; + console.log('ipc', service, method); + + if(![ + 'FileService', + 'SocketService', + 'StorageService', + 'WindowService', + ].includes(service)) return; // console.log('Propagated from embed: ', service, method, data, id); + + if(service === 'StorageService'){ + // Handled in main process + if(method === 'getScatter' || method === 'setScatter') return; + } + + const result = await services[service][method](...data); + WebViewService.get().send('scatter', {data:result, id}) + + }); + + // Logs only + ipcRenderer.on('scatterLog', async (e, payload) => { + console.log('ipc logs', payload); + }); + } + +} \ No newline at end of file diff --git a/src/views/Login.vue b/src/views/Login.vue index 5a25074c..3ac25143 100644 --- a/src/views/Login.vue +++ b/src/views/Login.vue @@ -128,6 +128,8 @@ import BackupService from "../services/utility/BackupService"; import * as UIActions from "../store/ui_actions"; + const {Wallet} = window.require('electron').remote.getGlobal('appShared'); + const STATES = { NEW_OR_LOGIN:'newOrLogin', CREATE_NEW:'createNew', @@ -213,48 +215,68 @@ })); }, - async unlock(usingLocalStorage = false){ - if(!usingLocalStorage){ - if(this.opening) return; - this.opening = true; + async unlock(){ + if(this.opening) return; + this.opening = true; + + const scatter = await Wallet.unlock(this.password); + + if(!scatter){ + this.opening = false; + this.badPassword = true; + PopupService.push(Popup.snackbarBadPassword()); + return; } - setTimeout(async () => { - await this[UIActions.SET_SEED](this.password); - await this[Actions.LOAD_SCATTER](usingLocalStorage); - if(typeof this.scatter === 'object' && !this.scatter.isEncrypted()){ - setTimeout(() => { - if(!this.scatter.onboarded){ - PopupService.push(Popup.showTerms(async accepted => { - if(!accepted){ - await this[UIActions.SET_SEED](null); - await this[Actions.LOAD_SCATTER](false); - this.opening = false; - return; - } - - const clone = this.scatter.clone(); - clone.onboarded = true; - await this[Actions.SET_SCATTER](clone); - - if(!this.scatter.settings.backupLocation.length){ - await BackupService.setDefaultBackupLocation(); - } - - this.success = true; - this.$router.push({name:this.RouteNames.SCATTER}); - })) - } else { - this.success = true; - this.$router.push({name:this.RouteNames.SCATTER}); - } - }, 1000); - } else { - if(!usingLocalStorage) return this.unlock(true); - this.opening = false; - this.badPassword = true; - PopupService.push(Popup.snackbarBadPassword()); - } - }, 400) + + await this[Actions.HOLD_SCATTER](scatter); + this.success = true; + this.$router.push({name:this.RouteNames.SCATTER}); + console.log('unlocked', scatter); + + + + + // if(!usingLocalStorage){ + // if(this.opening) return; + // this.opening = true; + // } + // setTimeout(async () => { + // await this[UIActions.SET_SEED](this.password); + // await this[Actions.LOAD_SCATTER](usingLocalStorage); + // if(typeof this.scatter === 'object' && !this.scatter.isEncrypted()){ + // setTimeout(() => { + // if(!this.scatter.onboarded){ + // PopupService.push(Popup.showTerms(async accepted => { + // if(!accepted){ + // await this[UIActions.SET_SEED](null); + // await this[Actions.LOAD_SCATTER](false); + // this.opening = false; + // return; + // } + // + // const clone = this.scatter.clone(); + // clone.onboarded = true; + // await this[Actions.SET_SCATTER](clone); + // + // if(!this.scatter.settings.backupLocation.length){ + // await BackupService.setDefaultBackupLocation(); + // } + // + // this.success = true; + // this.$router.push({name:this.RouteNames.SCATTER}); + // })) + // } else { + // this.success = true; + // this.$router.push({name:this.RouteNames.SCATTER}); + // } + // }, 1000); + // } else { + // if(!usingLocalStorage) return this.unlock(true); + // this.opening = false; + // this.badPassword = true; + // PopupService.push(Popup.snackbarBadPassword()); + // } + // }, 400) }, destroy(){ PopupService.push(Popup.destroyScatter()); @@ -265,6 +287,7 @@ UIActions.SET_SEED, Actions.LOAD_SCATTER, Actions.SET_SCATTER, + Actions.HOLD_SCATTER, ]) }, watch:{ diff --git a/src/views/PopOut.vue b/src/views/PopOut.vue index 5f2ed1a0..1459f1cb 100644 --- a/src/views/PopOut.vue +++ b/src/views/PopOut.vue @@ -1,167 +1,70 @@ diff --git a/src/views/Scatter.vue b/src/views/Scatter.vue index a5b98fe6..f9f7b655 100644 --- a/src/views/Scatter.vue +++ b/src/views/Scatter.vue @@ -1,89 +1,41 @@ diff --git a/src/views/popouts/GetPublicKey.vue b/src/views/popouts/GetPublicKey.vue deleted file mode 100644 index 4db841fa..00000000 --- a/src/views/popouts/GetPublicKey.vue +++ /dev/null @@ -1,92 +0,0 @@ - - - - - diff --git a/src/views/popouts/LinkApp.vue b/src/views/popouts/LinkApp.vue deleted file mode 100644 index 1b78d344..00000000 --- a/src/views/popouts/LinkApp.vue +++ /dev/null @@ -1,84 +0,0 @@ - - - - - \ No newline at end of file diff --git a/src/views/popouts/Signature.vue b/src/views/popouts/Signature.vue deleted file mode 100644 index e31829fb..00000000 --- a/src/views/popouts/Signature.vue +++ /dev/null @@ -1,666 +0,0 @@ - - - - - diff --git a/src/views/popouts/TransferRequest.vue b/src/views/popouts/TransferRequest.vue deleted file mode 100644 index ec676e0e..00000000 --- a/src/views/popouts/TransferRequest.vue +++ /dev/null @@ -1,279 +0,0 @@ - - - - - diff --git a/src/views/popouts/UpdateIdentity.vue b/src/views/popouts/UpdateIdentity.vue deleted file mode 100644 index b422e8ab..00000000 --- a/src/views/popouts/UpdateIdentity.vue +++ /dev/null @@ -1,110 +0,0 @@ - - - - - diff --git a/src/vue/VueInitializer.js b/src/vue/VueInitializer.js index f3b71fc5..9cad32db 100644 --- a/src/vue/VueInitializer.js +++ b/src/vue/VueInitializer.js @@ -6,20 +6,9 @@ import VueQrcodeReader from 'vue-qrcode-reader' import VueRouter from 'vue-router' import {RouteNames, Routing} from './Routing'; -const Helpers = require('../util/ElectronHelpers').default; import features from '../features'; -import * as Actions from '@walletpack/core/store/constants' -import {blockchainName, Blockchains} from '@walletpack/core/models/Blockchains' -import {SETTINGS_OPTIONS} from '@walletpack/core/models/Settings' -import StoreService from "@walletpack/core/services/utility/StoreService"; -import AppsService from "@walletpack/core/services/apps/AppsService"; -import {dateId} from "@walletpack/core/util/DateHelpers"; -import PriceService from "@walletpack/core/services/apis/PriceService"; -import * as UIActions from "../store/ui_actions"; - -// TODO: -const LANG_KEYS = {}; +import {store} from "../store/store"; Vue.config.productionTip = false @@ -33,108 +22,19 @@ export default class VueInitializer { constructor(routes, components, - middleware = () => {}, - routerCallback = () => {}){ + middleware = () => {}){ this.setupVuePlugins(); this.registerComponents(components); router = this.setupRouting(routes, middleware); - StoreService.get().dispatch(Actions.LOAD_SCATTER).then(async () => { - - - - Vue.mixin({ - data(){ return { - RouteNames, - SETTINGS_OPTIONS, - langKeys:LANG_KEYS, - loadingReputation:false, - features, - // now:0, - }}, - computed:{ - ...mapState([ - 'working', - 'priceData', - ]) - }, - methods: { - blockchainName, - back(){ this.$router.back(); }, - locale:(key, args) => { - console.log('getting locale for', key, args); - return 'NO LOCALES'; - }, - newKeypair(){ this.$router.push({name:RouteNames.NEW_KEYPAIR}); }, - canOpenApp(applink){ - const data = AppsService.getAppData(applink); - return data.url.length; - }, - fiatSymbol:PriceService.fiatSymbol, - getTokensTotaled(){ - if(!this.priceData || !this.priceData.hasOwnProperty('yesterday')) return []; - let totaled = []; - Object.keys(this.priceData.yesterday).filter(x => x !== 'latest').sort((a,b) => a - b).map(hour => - totaled.push({hour, data:this.priceData.yesterday[hour], date:dateId(1)})); - Object.keys(this.priceData.today).filter(x => x !== 'latest').sort((a,b) => a - b).map(hour => - totaled.push({hour, data:this.priceData.today[hour], date:dateId()})); - totaled = totaled.slice(totaled.length-(totaled.length > 24 ? 24 : totaled.length), totaled.length); - return totaled; - }, - openApp(applink){ - const data = AppsService.getAppData(applink); - if(data.url.length) this.openInBrowser(data.url); - }, - openInBrowser(url){ - Helpers.openLinkInBrowser(url); - }, - setWorkingScreen(bool){ StoreService.get().dispatch(UIActions.SET_WORKING_SCREEN, bool); }, - copyText(text){ Helpers.copy(text) }, - publicKeyForKeypair(keypair){ - if(!keypair) return null; - if(!keypair.hasOwnProperty('publicKeys')) return null; - return keypair.enabledKey().key; - }, - - - formatNumber(num, commaOnly = false){ - if(!num) return 0; - num = parseFloat(num.toString()); - const toComma = x => { - const [whole, decimal] = x.toString().split('.'); - return whole.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",") + (decimal ? `.${decimal}` : '').toString(); - } - if(commaOnly) return toComma(num); - return (num > 999999999 ? toComma((num/1000000000).toFixed(1)) + ' B' : - num > 999999 ? toComma((num/1000000).toFixed(1)) + ' M' : - num > 999 ? toComma((num/1000).toFixed(1)) + ' K' : num) - }, - formatTime(milliseconds){ - const formatTimeNumber = n => { - if(!n) return '00'; - if(n.toString().length === 1) n = '0'+n; - if(n.toString().length === 0) n = '00'; - return n; - }; - - const seconds = Math.trunc(milliseconds) % 60; - const minutes = Math.trunc(milliseconds / 60) % 60; - return `${formatTimeNumber(minutes)}:${formatTimeNumber(seconds)}`; - }, - - - ...mapActions([ - - ]) - } - }) - - this.setupVue(router); - - routerCallback(router, StoreService.get()); - }); + Vue.mixin({ + data(){ return { + RouteNames, + }}, + }) + this.setupVue(router); return router; } @@ -155,14 +55,13 @@ export default class VueInitializer { setupRouting(routes, middleware){ const router = new VueRouter({routes}); router.beforeEach((to, from, next) => { - StoreService.get().dispatch(UIActions.SET_SEARCH_TERMS, ''); - return middleware(to, next, StoreService.get()) + return middleware(to, next) }); return router; } setupVue(router){ - const app = new Vue({router, store:StoreService.get()}); + const app = new Vue({router, store}); app.$mount('#scatter'); // This removes the browser console's ability to diff --git a/yarn.lock b/yarn.lock index dc60de46..f1c73f6b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -880,18 +880,18 @@ source-map "~0.6.1" vue-template-es2015-compiler "^1.9.0" -"@walletpack/bitcoin@^1.0.19": - version "1.0.19" - resolved "https://registry.yarnpkg.com/@walletpack/bitcoin/-/bitcoin-1.0.19.tgz#dcb5b8323c2ea81ce02788ce2956dbed8e0d707c" - integrity sha512-SaqhVd1L3CENXBJt9r01FEXCrGI+hGLCoOqDaa/cC1tIaR63H6A9zh7m+FSeUv5uiQwcCa/p9k+7JVnah+x9Rg== +"@walletpack/bitcoin@^1.0.20": + version "1.0.20" + resolved "https://registry.yarnpkg.com/@walletpack/bitcoin/-/bitcoin-1.0.20.tgz#50ccdf0889e804a3d4a3084a5e170b9cc24692d8" + integrity sha512-aYXeZENBy/aP+i7Ap/43Ccdi9dLACb4moV8C8CY56bDsKIbuBTKo4WnwPn0Jxtx14GAP+JixuBrhLzM97tYVUA== dependencies: - "@walletpack/core" "^1.0.18" + "@walletpack/core" "^1.0.19" bitcoinjs-lib "^5.1.2" -"@walletpack/core@^1.0.18": - version "1.0.18" - resolved "https://registry.yarnpkg.com/@walletpack/core/-/core-1.0.18.tgz#c712dd0eb45c29bdab6c23497174fb8073c068e3" - integrity sha512-S5cD76ME7QPblCnGknURK/R1cXB5QywOw5o09yVi9QfxZJI2iU5kW+vFyRu8twWcpHBx32Kr5zdyK/eRfBUYWw== +"@walletpack/core@^1.0.19": + version "1.0.19" + resolved "https://registry.yarnpkg.com/@walletpack/core/-/core-1.0.19.tgz#5eb072679387681424ab08132894c5947f8a5f84" + integrity sha512-S3Ej1J65cfOa4i9BAn/sExfamQ77TN/GGWgja0gV1O7iZ0jXls0dA8RRQmEeWHsW/2wUaGfaFfsZTcXC3ymKKQ== dependencies: aes-oop "^1.0.4" bignumber.js "^9.0.0" @@ -900,32 +900,32 @@ qrcode "^1.4.1" scrypt-async "^2.0.1" -"@walletpack/eosio@^0.0.19": - version "0.0.19" - resolved "https://registry.yarnpkg.com/@walletpack/eosio/-/eosio-0.0.19.tgz#1361617805ae483cef48152487b784bb94c53901" - integrity sha512-YVdTFXSYbrHdGLEGXbiwUTZVyZxy+9x185wFHwWLJSHxbadGY+qKlVKPFcoAkzx7mdhhqFuFWaf555OvvqAGEA== +"@walletpack/eosio@^0.0.20": + version "0.0.20" + resolved "https://registry.yarnpkg.com/@walletpack/eosio/-/eosio-0.0.20.tgz#c80246142f56a2510436b6ef6a5e278ba1aa04e5" + integrity sha512-Qh3EBPwQmqGca5tBnk+VrFsK9ABOpModaF+KGxdedo4J2fkpnQpRN4SYfahnmKtNTdFVDMuZc0+l27iiX81zJw== dependencies: - "@walletpack/core" "^1.0.18" + "@walletpack/core" "^1.0.19" eosjs "^20.0.0" eosjs-ecc "^4.0.4" -"@walletpack/ethereum@^0.0.19": - version "0.0.19" - resolved "https://registry.yarnpkg.com/@walletpack/ethereum/-/ethereum-0.0.19.tgz#5d1196ea9f59454a654d3e414d9bb613a1bcc225" - integrity sha512-4W87iA/gmy0WEm5DldCTeabtFsGeuV8uDRd78f5m7mliMUTLkhpW875bShxGWV1iXrd5/G/YJB3TdKIT/IFLWg== +"@walletpack/ethereum@^0.0.20": + version "0.0.20" + resolved "https://registry.yarnpkg.com/@walletpack/ethereum/-/ethereum-0.0.20.tgz#241d4535476f6ff3ba6f29deac1461c56d430853" + integrity sha512-OP4f2P8HR9zoTL5U8r7kMLSMTW9XxLpgtNC8Nn04AAqbxo2msxYDpMMlmVi+/13ooJJ8IQnX8k/eRJLFTy0Okw== dependencies: - "@walletpack/core" "^1.0.18" + "@walletpack/core" "^1.0.19" ethereumjs-tx "^2.1.0" ethereumjs-util "^6.1.0" web3 "^1.2.0" web3-provider-engine "^15.0.0" -"@walletpack/tron@^0.0.19": - version "0.0.19" - resolved "https://registry.yarnpkg.com/@walletpack/tron/-/tron-0.0.19.tgz#2b1b5cd5274a55c156220fbdb1116f6a170e1911" - integrity sha512-YnrLOxkUu1ds01cFhSQAUIobzJPrgRqqtg5FG6HpI6Z7CRqKI3pK2bHEP+D8BLDIK/txmT6hWHvtWe6SQap4JA== +"@walletpack/tron@^0.0.20": + version "0.0.20" + resolved "https://registry.yarnpkg.com/@walletpack/tron/-/tron-0.0.20.tgz#9f06f4438950e2cf1016ce05b2c6213408ccd374" + integrity sha512-8/BZp5e0e14k/hpjEIBJq+ZnXTeYdPrn4wPzfs2d79GAmgMQHfxoJUSQh40SBA6/HizNEQz6hahJL8gMFeHlug== dependencies: - "@walletpack/core" "^1.0.18" + "@walletpack/core" "^1.0.19" ethereumjs-util "^6.1.0" tronweb "^2.6.4" From c568d28d8ac401036036a7f6b88bbc27b4af162d Mon Sep 17 00:00:00 2001 From: nsjames Date: Sun, 1 Sep 2019 17:40:31 +0300 Subject: [PATCH 03/72] signers --- electron/app.js | 5 ++++ electron/services/wallet.js | 37 +++++++++++++++++++++++++++- package.json | 10 ++++---- src/util/WalletTalk.js | 2 +- yarn.lock | 48 ++++++++++++++++++------------------- 5 files changed, 71 insertions(+), 31 deletions(-) diff --git a/electron/app.js b/electron/app.js index d8e7621b..d4cacd7a 100644 --- a/electron/app.js +++ b/electron/app.js @@ -216,11 +216,16 @@ global.scatterMessage = async (data) => { return null; } + if(data.service === 'sign' && data.method === 'sign') { + return {data:await wallet.sign(...data.data), id:data.id}; + } + if(data.method === 'getScatter') return {data:wallet.getScatter(), id:data.id}; // Popouts can only get scatter data, not update it if(data.isPopOut) return; + if(data.method === 'sign') return {data:await wall.sign(...data.data), id:data.id}; if(data.method === 'setScatter') return {data:await wallet.updateScatter(...data.data), id:data.id}; mainWindow.webContents.send('scatter', data); diff --git a/electron/services/wallet.js b/electron/services/wallet.js index 2353c0cb..4bc109ff 100644 --- a/electron/services/wallet.js +++ b/electron/services/wallet.js @@ -8,8 +8,22 @@ const AES = require("aes-oop").default; const storage = require('./storage') const path = require('path') const Scatter = require('@walletpack/core/models/Scatter').default; +const PluginRepository = require('@walletpack/core/plugins/PluginRepository').default; +const Error = require('@walletpack/core/models/errors/Error').default; +const EOSIO = require('@walletpack/eosio').default; +const TRON = require('@walletpack/tron').default; +const BITCOIN = require('@walletpack/bitcoin').default; +const ETHEREUM = require('@walletpack/ethereum').default; + +const plugins = { + eos:new EOSIO(), + trx:new TRON(), + btc:new BITCOIN(), + eth:new ETHEREUM() +} + let seed, salt; let scatter = storage.getScatter(); @@ -94,8 +108,29 @@ const unlock = async password => { } } -const sign = () => { +const sign = (network, publicKey, payload, arbitrary = false, isHash = false) => { + try { + + const plugin = plugins[network.blockchain]; + console.log('plugin', plugin, network.blockchain); + if(!plugin) return false; + const keypair = scatter.keychain.keypairs.find(x => x.publicKeys.find(k => k.key === publicKey)) + console.log('keypair', !!keypair); + if(!keypair) return Error.signatureError('no_keypair', 'This keypair could not be found'); + + let privateKey = AES.decrypt(keypair.privateKey, seed); + if(!privateKey) return Error.signatureError('sign_err', 'Could not decode private key'); + if(typeof privateKey === 'object' && privateKey.hasOwnProperty('data')) privateKey = privateKey.data; + + + // TODO: eosjs-ecc needs an update with a patch for ripemd, for now edit 'eosjs-ecc/hash.js' + // https://github.com/EOSIO/eosjs-ecc/pull/56/commits/a4f409927bc84022d5fc9811dfccec85b7ffbb2b + return plugin.signer(payload, publicKey, arbitrary, isHash, privateKey); + } catch(e){ + console.error('Signing Error!', e); + return Error.signatureError('sign_err', 'There was an error signing this transaction.'); + } }; ipcMain.on('load', (e) => { diff --git a/package.json b/package.json index 11cb4bc0..0166d574 100644 --- a/package.json +++ b/package.json @@ -25,11 +25,11 @@ "@babel/runtime": "^7.5.4", "@ledgerhq/hw-app-eth": "^4.68.4", "@ledgerhq/hw-transport-node-hid": "^4.55.0", - "@walletpack/bitcoin": "^1.0.20", - "@walletpack/core": "^1.0.19", - "@walletpack/eosio": "^0.0.20", - "@walletpack/ethereum": "^0.0.20", - "@walletpack/tron": "^0.0.20", + "@walletpack/bitcoin": "^1.0.24", + "@walletpack/core": "^1.0.22", + "@walletpack/eosio": "^0.0.24", + "@walletpack/ethereum": "^0.0.24", + "@walletpack/tron": "^0.0.24", "aes-oop": "^1.0.4", "asn1-ber": "^1.0.9", "babel-polyfill": "^6.26.0", diff --git a/src/util/WalletTalk.js b/src/util/WalletTalk.js index a4f42e02..477f4d56 100644 --- a/src/util/WalletTalk.js +++ b/src/util/WalletTalk.js @@ -20,7 +20,7 @@ export default class WalletTalk { static setup(){ ipcRenderer.on('popoutResponse', async (e, payload) => { console.log('popoutresponse', payload); - WebViewService.get().send('scatter', {data:payload.result, id:payload.original.id}) + WebViewService.get().send('scatter', {data:payload.data.result, id:payload.data.original.id}) }); ipcRenderer.on('scatter', async (e, payload) => { diff --git a/yarn.lock b/yarn.lock index f1c73f6b..68e2ca42 100644 --- a/yarn.lock +++ b/yarn.lock @@ -880,18 +880,18 @@ source-map "~0.6.1" vue-template-es2015-compiler "^1.9.0" -"@walletpack/bitcoin@^1.0.20": - version "1.0.20" - resolved "https://registry.yarnpkg.com/@walletpack/bitcoin/-/bitcoin-1.0.20.tgz#50ccdf0889e804a3d4a3084a5e170b9cc24692d8" - integrity sha512-aYXeZENBy/aP+i7Ap/43Ccdi9dLACb4moV8C8CY56bDsKIbuBTKo4WnwPn0Jxtx14GAP+JixuBrhLzM97tYVUA== +"@walletpack/bitcoin@^1.0.24": + version "1.0.24" + resolved "https://registry.yarnpkg.com/@walletpack/bitcoin/-/bitcoin-1.0.24.tgz#995ebc4d8e01ae5718e32b8eb0ed1c9cc8d4c3c3" + integrity sha512-QsWYn/i+VqIvQKK6vC/1099744gqmnV7JH/+P6Sxy9EL3EWq9ZO9FLCxSeCy6FFAm/+geIW2NJ7evqV2e+m7yA== dependencies: - "@walletpack/core" "^1.0.19" + "@walletpack/core" "^1.0.22" bitcoinjs-lib "^5.1.2" -"@walletpack/core@^1.0.19": - version "1.0.19" - resolved "https://registry.yarnpkg.com/@walletpack/core/-/core-1.0.19.tgz#5eb072679387681424ab08132894c5947f8a5f84" - integrity sha512-S3Ej1J65cfOa4i9BAn/sExfamQ77TN/GGWgja0gV1O7iZ0jXls0dA8RRQmEeWHsW/2wUaGfaFfsZTcXC3ymKKQ== +"@walletpack/core@^1.0.22", "@walletpack/core@^1.0.23": + version "1.0.22" + resolved "https://registry.yarnpkg.com/@walletpack/core/-/core-1.0.22.tgz#b41d46315ef6ef81eb283a43a6e442ef6599a44c" + integrity sha512-SgwFq544fZ0YUa1SWhDnsmc6jRCZFGT7DXLJD6FsAAhxe7zGd8T+q++t+6+fWja4Os/3/aNkHx4OGDNwFPVH+w== dependencies: aes-oop "^1.0.4" bignumber.js "^9.0.0" @@ -900,32 +900,32 @@ qrcode "^1.4.1" scrypt-async "^2.0.1" -"@walletpack/eosio@^0.0.20": - version "0.0.20" - resolved "https://registry.yarnpkg.com/@walletpack/eosio/-/eosio-0.0.20.tgz#c80246142f56a2510436b6ef6a5e278ba1aa04e5" - integrity sha512-Qh3EBPwQmqGca5tBnk+VrFsK9ABOpModaF+KGxdedo4J2fkpnQpRN4SYfahnmKtNTdFVDMuZc0+l27iiX81zJw== +"@walletpack/eosio@^0.0.24": + version "0.0.24" + resolved "https://registry.yarnpkg.com/@walletpack/eosio/-/eosio-0.0.24.tgz#df4409afed34f0cd4a4e11ca34dfd2dbc7868bcb" + integrity sha512-MsaG0+K4UrWY49XLwlXG3ANNCtCTrdJUC2xMTONg2BhyqNsQtWPXfg6+9pYwnZs9XIxZ7ef7M98MC0LFUWdV4A== dependencies: - "@walletpack/core" "^1.0.19" + "@walletpack/core" "^1.0.22" eosjs "^20.0.0" eosjs-ecc "^4.0.4" -"@walletpack/ethereum@^0.0.20": - version "0.0.20" - resolved "https://registry.yarnpkg.com/@walletpack/ethereum/-/ethereum-0.0.20.tgz#241d4535476f6ff3ba6f29deac1461c56d430853" - integrity sha512-OP4f2P8HR9zoTL5U8r7kMLSMTW9XxLpgtNC8Nn04AAqbxo2msxYDpMMlmVi+/13ooJJ8IQnX8k/eRJLFTy0Okw== +"@walletpack/ethereum@^0.0.24": + version "0.0.24" + resolved "https://registry.yarnpkg.com/@walletpack/ethereum/-/ethereum-0.0.24.tgz#74d81c59a217570e7701e12501e7aa5b150937d3" + integrity sha512-s7K1OrMGncn5R3OjiM2CIjFCk3RRNnEYVYJ2TevBksk7YUjPXavVc3dt+YhqwwHOE4pRVJOVkxnETq7uhjnfFQ== dependencies: - "@walletpack/core" "^1.0.19" + "@walletpack/core" "^1.0.22" ethereumjs-tx "^2.1.0" ethereumjs-util "^6.1.0" web3 "^1.2.0" web3-provider-engine "^15.0.0" -"@walletpack/tron@^0.0.20": - version "0.0.20" - resolved "https://registry.yarnpkg.com/@walletpack/tron/-/tron-0.0.20.tgz#9f06f4438950e2cf1016ce05b2c6213408ccd374" - integrity sha512-8/BZp5e0e14k/hpjEIBJq+ZnXTeYdPrn4wPzfs2d79GAmgMQHfxoJUSQh40SBA6/HizNEQz6hahJL8gMFeHlug== +"@walletpack/tron@^0.0.24": + version "0.0.24" + resolved "https://registry.yarnpkg.com/@walletpack/tron/-/tron-0.0.24.tgz#2865692bef0f0517893d990ac3815167ea3da397" + integrity sha512-iIyZ0z0H3SGH+gDKfYQWqO2rRXzQ0Q5nJYvtrC36iQXhAb0tFHvyZkLszuLbeMMC2YVjpu6g/sEuzkGi3Xi5kQ== dependencies: - "@walletpack/core" "^1.0.19" + "@walletpack/core" "^1.0.22" ethereumjs-util "^6.1.0" tronweb "^2.6.4" From 28fb97627040f53605b185e297ab393a7b02da84 Mon Sep 17 00:00:00 2001 From: nsjames Date: Fri, 6 Sep 2019 09:20:41 +0300 Subject: [PATCH 04/72] work --- electron/app.js | 15 ++++++--------- src/services/electron/WindowService.js | 1 + src/util/ElectronHelpers.js | 5 ++--- src/util/WalletTalk.js | 3 ++- src/views/PopOut.vue | 2 +- src/views/Scatter.vue | 6 +----- 6 files changed, 13 insertions(+), 19 deletions(-) diff --git a/electron/app.js b/electron/app.js index d4cacd7a..2e9dcb4c 100644 --- a/electron/app.js +++ b/electron/app.js @@ -211,21 +211,18 @@ const wallet = require('./services/wallet'); // FORWARDING FROM INJECTED DOM global.scatterMessage = async (data) => { - if(data.service === 'popout' && data.method === 'response') { - mainWindow.webContents.send('popoutResponse', data); - return null; - } - - if(data.service === 'sign' && data.method === 'sign') { - return {data:await wallet.sign(...data.data), id:data.id}; - } + console.log('got data',data); + if(data.service === 'popout' && data.method === 'response') { mainWindow.webContents.send('popoutResponse', data); return null; } if(data.method === 'getScatter') return {data:wallet.getScatter(), id:data.id}; // Popouts can only get scatter data, not update it if(data.isPopOut) return; - if(data.method === 'sign') return {data:await wall.sign(...data.data), id:data.id}; + if(data.service === 'sign' && data.method === 'sign') { + console.log('sign result', await wallet.sign(...data.data)); + return {data:await wallet.sign(...data.data), id:data.id}; + } if(data.method === 'setScatter') return {data:await wallet.updateScatter(...data.data), id:data.id}; mainWindow.webContents.send('scatter', data); diff --git a/src/services/electron/WindowService.js b/src/services/electron/WindowService.js index c9d458b4..40078c8f 100644 --- a/src/services/electron/WindowService.js +++ b/src/services/electron/WindowService.js @@ -14,6 +14,7 @@ let popouts = []; ipcRenderer.on('popoutResponse', (event, data) => { + console.log('PPPRESSSSPPPP', data); if(!data) return; const result = data.data; if(!result) return; diff --git a/src/util/ElectronHelpers.js b/src/util/ElectronHelpers.js index db76e495..b1165581 100644 --- a/src/util/ElectronHelpers.js +++ b/src/util/ElectronHelpers.js @@ -77,11 +77,10 @@ export default class ElectronHelpers { const eventListener = async (type, data) => { + // console.log('event listener', type, data); if(type === 'popout') { - console.log('POPOUT!') const popup = new Popup(PopupDisplayTypes.POP_OUT, new PopupData(data.type, data)); - const popoutResult = await WindowService.openPopOut(popup); - console.log("POPOUT RESULT?", popoutResult); + return await WindowService.openPopOut(popup); } }; diff --git a/src/util/WalletTalk.js b/src/util/WalletTalk.js index 477f4d56..c47c54d7 100644 --- a/src/util/WalletTalk.js +++ b/src/util/WalletTalk.js @@ -20,7 +20,7 @@ export default class WalletTalk { static setup(){ ipcRenderer.on('popoutResponse', async (e, payload) => { console.log('popoutresponse', payload); - WebViewService.get().send('scatter', {data:payload.data.result, id:payload.data.original.id}) + WebViewService.get().send('scatter', {data:payload.data.result, id:payload.id}) }); ipcRenderer.on('scatter', async (e, payload) => { @@ -40,6 +40,7 @@ export default class WalletTalk { } const result = await services[service][method](...data); + console.log('REEEEEESSSULLLLTTTTTT', result, id, payload); WebViewService.get().send('scatter', {data:result, id}) }); diff --git a/src/views/PopOut.vue b/src/views/PopOut.vue index 1459f1cb..40e52b34 100644 --- a/src/views/PopOut.vue +++ b/src/views/PopOut.vue @@ -36,7 +36,7 @@ setup(){ WebViewService.set(this.$refs.webview); WebViewService.get().addEventListener('dom-ready', () => { - // WebViewService.get().openDevTools(); + WebViewService.get().openDevTools(); WebViewService.get().executeJavaScript('window.injector();'); WebViewService.get().send('popout', this.popOut); }) diff --git a/src/views/Scatter.vue b/src/views/Scatter.vue index f9f7b655..053daf3d 100644 --- a/src/views/Scatter.vue +++ b/src/views/Scatter.vue @@ -18,11 +18,7 @@ mounted(){ WebViewService.set(this.$refs.webview); WebViewService.get().addEventListener('dom-ready', () => { - // WebViewService.get().openDevTools(); - // setTimeout(() => { - // WebViewService.get().executeJavaScript('window.injector();'); - // }, 1000); - + WebViewService.get().openDevTools(); WebViewService.get().executeJavaScript('window.injector();'); }) From ad99ccaee9ff2ed4c56b704af2226a4b3c67cac9 Mon Sep 17 00:00:00 2001 From: nsjames Date: Fri, 6 Sep 2019 12:17:20 +0300 Subject: [PATCH 05/72] fixes --- electron/app.js | 17 ++++----- electron/services/wallet.js | 15 ++++---- electron/utils.js | 6 ++-- package.json | 6 ++-- src/components/MenuBar.vue | 13 ------- src/main.js | 1 - src/services/electron/Injectable.js | 17 +++++++++ src/services/electron/WebViewService.js | 1 - src/services/electron/WindowService.js | 3 -- src/store/actions.js | 2 +- src/util/ElectronHelpers.js | 3 -- src/util/WalletTalk.js | 8 ++--- src/views/Login.vue | 46 ------------------------- src/views/PopOut.vue | 4 +-- src/views/Scatter.vue | 2 +- yarn.lock | 2 +- 16 files changed, 44 insertions(+), 102 deletions(-) create mode 100644 src/services/electron/Injectable.js diff --git a/electron/app.js b/electron/app.js index 2e9dcb4c..d6d20d74 100644 --- a/electron/app.js +++ b/electron/app.js @@ -1,13 +1,11 @@ -const LowLevelWindowService = require("./services/windows"); -const LowLevelSocketService = require('./services/sockets'); -const NotificationService = require('./services/notifier'); - - require("babel-polyfill") - const electron = require('electron'); const {remote, app, BrowserWindow, Tray, Menu, MenuItem, ipcMain} = electron; + const {isDev, icon, trayIcon, mainUrl} = require('./utils'); +const LowLevelWindowService = require("./services/windows"); +const LowLevelSocketService = require('./services/sockets'); +const NotificationService = require('./services/notifier'); @@ -216,13 +214,10 @@ global.scatterMessage = async (data) => { if(data.service === 'popout' && data.method === 'response') { mainWindow.webContents.send('popoutResponse', data); return null; } if(data.method === 'getScatter') return {data:wallet.getScatter(), id:data.id}; - // Popouts can only get scatter data, not update it + // Popouts can only get scatter data if(data.isPopOut) return; - if(data.service === 'sign' && data.method === 'sign') { - console.log('sign result', await wallet.sign(...data.data)); - return {data:await wallet.sign(...data.data), id:data.id}; - } + if(data.service === 'sign' && data.method === 'sign') return {data:await wallet.sign(...data.data), id:data.id}; if(data.method === 'setScatter') return {data:await wallet.updateScatter(...data.data), id:data.id}; mainWindow.webContents.send('scatter', data); diff --git a/electron/services/wallet.js b/electron/services/wallet.js index 4bc109ff..c1a5e9e2 100644 --- a/electron/services/wallet.js +++ b/electron/services/wallet.js @@ -31,7 +31,7 @@ let scatter = storage.getScatter(); salt = storage.getSalt(); const setScatter = (_s) => scatter = JSON.parse(JSON.stringify(_s)); -const getScatter = () => JSON.parse(JSON.stringify(scatter)); +const getScatter = () => scatter ? JSON.parse(JSON.stringify(scatter)) : null; const updateScatter = async (_s) => { const isEncrypted = x => x.toString().indexOf('"iv":') > -1 @@ -66,6 +66,7 @@ const updateScatter = async (_s) => { const hashPassword = (password) => { return new Promise(async resolve => { + salt = storage.getSalt(); scrypt(password, salt, { N: 16384, r: 8, @@ -87,13 +88,15 @@ const passwordToSeed = async password => { -const unlock = async password => { +const unlock = async (password, isNew = false) => { try { seed = await passwordToSeed(password); - const decrypted = AES.decrypt(scatter, seed); - if(!decrypted.hasOwnProperty('keychain')) return false; - decrypted.keychain = AES.decrypt(decrypted.keychain, seed); - scatter = decrypted; + if(!isNew) { + const decrypted = AES.decrypt(scatter, seed); + if (!decrypted.hasOwnProperty('keychain')) return false; + decrypted.keychain = AES.decrypt(decrypted.keychain, seed); + scatter = decrypted; + } setTimeout(() => { LowLevelWindowService.queuePopup(); diff --git a/electron/utils.js b/electron/utils.js index e4fcb31a..48316149 100644 --- a/electron/utils.js +++ b/electron/utils.js @@ -5,14 +5,14 @@ const isDev = process.mainModule.filename.indexOf('app.asar') === -1; let icon = isDev ? 'static/icons/icon.png' - : __dirname + '/static/icons/icon.png'; + : __dirname + '/../static/icons/icon.png'; let trayIcon = isDev ? 'static/icons/icon-tray.png' - : __dirname + '/static/icons/icon-tray.png'; + : __dirname + '/../static/icons/icon-tray.png'; let mainUrl = isPopup => isDev ? `http://localhost:8080/${isPopup ? '/#/popout' : ''}` : url.format({ - pathname: path.join(__dirname, "dist", "index.html"), + pathname: __dirname + '/../dist/index.html', protocol: "file:", slashes: true, hash:isPopup ? '/popout' : null diff --git a/package.json b/package.json index 0166d574..0dfe6cc2 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "scatter", - "version": "11.0.1", + "version": "11.1.0", "private": true, "buttonText": "Scatter Desktop Companion", "author": { @@ -143,16 +143,16 @@ }, "files": [ "dist/**/*", + "electron/**/*", "static/**/*", "electron.js", + "preload.js", "package.json" ], "publish": { "provider": "github" }, "nsis": { - "deleteAppDataOnUninstall": true, - "allowToChangeInstallationDirectory": true, "oneClick": false, "createDesktopShortcut": "always", "createStartMenuShortcut": true, diff --git a/src/components/MenuBar.vue b/src/components/MenuBar.vue index c152be54..362b8dd5 100644 --- a/src/components/MenuBar.vue +++ b/src/components/MenuBar.vue @@ -5,13 +5,6 @@
- -
-
-
-
-
-
@@ -52,12 +45,6 @@ }, 500); }, methods:{ - test(){ - this.$router.push({name:this.RouteNames.LOGIN}); - setTimeout(() => { - this.$router.push({name:this.RouteNames.SCATTER}); - },10) - }, quit(){ SocketService.broadcastEvent('dced', {}); setTimeout(() => remote.app.quit(), 1); diff --git a/src/main.js b/src/main.js index 1bd1e2c2..7b339ede 100644 --- a/src/main.js +++ b/src/main.js @@ -72,7 +72,6 @@ class Main { Helpers.initializeCore(); ipcRenderer.on('loaded', (e,payload) => { - console.log('loaded', payload); store.dispatch(Actions.HOLD_SCATTER, payload); }) ipcRenderer.send('load'); diff --git a/src/services/electron/Injectable.js b/src/services/electron/Injectable.js new file mode 100644 index 00000000..7c2af5e1 --- /dev/null +++ b/src/services/electron/Injectable.js @@ -0,0 +1,17 @@ +import ElectronHelpers from "../../util/ElectronHelpers"; + +export default class Injectable { + + static async appPath(){ + return ElectronHelpers.getDefaultPath(); + } + + static async openLink(link){ + return ElectronHelpers.openLinkInBrowser(link); + } + + static async copy(text){ + return ElectronHelpers.copy(text); + } + +} \ No newline at end of file diff --git a/src/services/electron/WebViewService.js b/src/services/electron/WebViewService.js index 5b571d79..0504b3ad 100644 --- a/src/services/electron/WebViewService.js +++ b/src/services/electron/WebViewService.js @@ -2,7 +2,6 @@ let webView; export default class WebViewService { static set(_wv){ - console.log('setting wv', webView, _wv) webView = _wv; } diff --git a/src/services/electron/WindowService.js b/src/services/electron/WindowService.js index 40078c8f..d8267fb7 100644 --- a/src/services/electron/WindowService.js +++ b/src/services/electron/WindowService.js @@ -14,13 +14,10 @@ let popouts = []; ipcRenderer.on('popoutResponse', (event, data) => { - console.log('PPPRESSSSPPPP', data); if(!data) return; const result = data.data; if(!result) return; - console.log('RESULT', result); - const popout = popouts.find(x => x.id === result.original.id); if(popout){ popouts = popouts.filter(x => x.id !== result.original.id); diff --git a/src/store/actions.js b/src/store/actions.js index e2d6317b..2eb19ad0 100644 --- a/src/store/actions.js +++ b/src/store/actions.js @@ -17,7 +17,7 @@ export const actions = { await Storage.setSalt(Hasher.unsaltedQuickHash(IdGenerator.text(32))); - await Wallet.unlock(password); + await Wallet.unlock(password, true); dispatch(Actions.SET_SCATTER, scatter).then(async _scatter => { await BackupService.setDefaultBackupLocation(); resolve(); diff --git a/src/util/ElectronHelpers.js b/src/util/ElectronHelpers.js index b1165581..b4659908 100644 --- a/src/util/ElectronHelpers.js +++ b/src/util/ElectronHelpers.js @@ -1,8 +1,6 @@ import {Popup, PopupData, PopupDisplayTypes} from "../models/popups/Popup"; let electron = window.require('electron'); -console.log('electron', electron); - export const remote = electron.remote; export const ipcRenderer = electron.ipcRenderer; const {clipboard, shell} = electron; @@ -116,7 +114,6 @@ export default class ElectronHelpers { { socketService:SocketService, publicToPrivate:async publicKey => { - console.log('pubkey', publicKey); return false; } } diff --git a/src/util/WalletTalk.js b/src/util/WalletTalk.js index c47c54d7..7dcc7c78 100644 --- a/src/util/WalletTalk.js +++ b/src/util/WalletTalk.js @@ -3,9 +3,6 @@ const electron = window.require('electron'); const {ipcRenderer} = electron; import WebViewService from "../services/electron/WebViewService"; -import Scatter from "@walletpack/core/models/Scatter"; -import * as Actions from "@walletpack/core/store/constants"; -import {store} from "../store/store"; const services = { @@ -13,25 +10,25 @@ const services = { SocketService:require('../services/electron/SocketService').default, StorageService:require('../services/electron/StorageService').default, WindowService:require('../services/electron/WindowService').default, + Injectable:require('../services/electron/Injectable').default, } export default class WalletTalk { static setup(){ ipcRenderer.on('popoutResponse', async (e, payload) => { - console.log('popoutresponse', payload); WebViewService.get().send('scatter', {data:payload.data.result, id:payload.id}) }); ipcRenderer.on('scatter', async (e, payload) => { const {service, method, data, id, isPopOut} = payload; - console.log('ipc', service, method); if(![ 'FileService', 'SocketService', 'StorageService', 'WindowService', + 'Injectable', ].includes(service)) return; // console.log('Propagated from embed: ', service, method, data, id); if(service === 'StorageService'){ @@ -40,7 +37,6 @@ export default class WalletTalk { } const result = await services[service][method](...data); - console.log('REEEEEESSSULLLLTTTTTT', result, id, payload); WebViewService.get().send('scatter', {data:result, id}) }); diff --git a/src/views/Login.vue b/src/views/Login.vue index 3ac25143..4cdb7838 100644 --- a/src/views/Login.vue +++ b/src/views/Login.vue @@ -231,52 +231,6 @@ await this[Actions.HOLD_SCATTER](scatter); this.success = true; this.$router.push({name:this.RouteNames.SCATTER}); - console.log('unlocked', scatter); - - - - - // if(!usingLocalStorage){ - // if(this.opening) return; - // this.opening = true; - // } - // setTimeout(async () => { - // await this[UIActions.SET_SEED](this.password); - // await this[Actions.LOAD_SCATTER](usingLocalStorage); - // if(typeof this.scatter === 'object' && !this.scatter.isEncrypted()){ - // setTimeout(() => { - // if(!this.scatter.onboarded){ - // PopupService.push(Popup.showTerms(async accepted => { - // if(!accepted){ - // await this[UIActions.SET_SEED](null); - // await this[Actions.LOAD_SCATTER](false); - // this.opening = false; - // return; - // } - // - // const clone = this.scatter.clone(); - // clone.onboarded = true; - // await this[Actions.SET_SCATTER](clone); - // - // if(!this.scatter.settings.backupLocation.length){ - // await BackupService.setDefaultBackupLocation(); - // } - // - // this.success = true; - // this.$router.push({name:this.RouteNames.SCATTER}); - // })) - // } else { - // this.success = true; - // this.$router.push({name:this.RouteNames.SCATTER}); - // } - // }, 1000); - // } else { - // if(!usingLocalStorage) return this.unlock(true); - // this.opening = false; - // this.badPassword = true; - // PopupService.push(Popup.snackbarBadPassword()); - // } - // }, 400) }, destroy(){ PopupService.push(Popup.destroyScatter()); diff --git a/src/views/PopOut.vue b/src/views/PopOut.vue index 40e52b34..68a21814 100644 --- a/src/views/PopOut.vue +++ b/src/views/PopOut.vue @@ -24,8 +24,6 @@ preload:() => `file://${path.join(remote.app.getAppPath(), 'preload.js')}`, }, beforeMount(){ - console.log('scatter', this.scatter); - ipcRenderer.on('ready', (event, popOut) => { this.popOut = popOut; this.ready = true; @@ -36,7 +34,7 @@ setup(){ WebViewService.set(this.$refs.webview); WebViewService.get().addEventListener('dom-ready', () => { - WebViewService.get().openDevTools(); + // WebViewService.get().openDevTools(); WebViewService.get().executeJavaScript('window.injector();'); WebViewService.get().send('popout', this.popOut); }) diff --git a/src/views/Scatter.vue b/src/views/Scatter.vue index 053daf3d..6355c7d2 100644 --- a/src/views/Scatter.vue +++ b/src/views/Scatter.vue @@ -18,7 +18,7 @@ mounted(){ WebViewService.set(this.$refs.webview); WebViewService.get().addEventListener('dom-ready', () => { - WebViewService.get().openDevTools(); + // WebViewService.get().openDevTools(); WebViewService.get().executeJavaScript('window.injector();'); }) diff --git a/yarn.lock b/yarn.lock index 68e2ca42..f730fb66 100644 --- a/yarn.lock +++ b/yarn.lock @@ -888,7 +888,7 @@ "@walletpack/core" "^1.0.22" bitcoinjs-lib "^5.1.2" -"@walletpack/core@^1.0.22", "@walletpack/core@^1.0.23": +"@walletpack/core@^1.0.22": version "1.0.22" resolved "https://registry.yarnpkg.com/@walletpack/core/-/core-1.0.22.tgz#b41d46315ef6ef81eb283a43a6e442ef6599a44c" integrity sha512-SgwFq544fZ0YUa1SWhDnsmc6jRCZFGT7DXLJD6FsAAhxe7zGd8T+q++t+6+fWja4Os/3/aNkHx4OGDNwFPVH+w== From 7db9b5e3bbd623b6eabca5c2810295dbcecfcb3a Mon Sep 17 00:00:00 2001 From: nsjames Date: Wed, 18 Sep 2019 14:23:39 +0300 Subject: [PATCH 06/72] updates --- .babelrc | 18 -- babel.config.js | 28 ++ build/webpack.config.base.js | 6 +- electron/app.js | 8 +- .../hardware/LedgerWallet.js | 183 +++++------- electron/services/hardware.js | 93 ++++++ electron/services/storage.js | 2 - electron/services/wallet.js | 121 +++++++- index.html | 13 + package.json | 12 +- preload.js | 14 +- src/components/MenuBar.vue | 15 + src/components/Popups.vue | 6 + src/components/login/SetPassword.vue | 15 +- .../popins/fullscreen/ConfirmPassword.vue | 73 +++++ .../popins/fullscreen/ExportPrivateKey.vue | 281 ++++++++++++++++++ .../popins/fullscreen/ImportBackup.vue | 79 +++-- src/main.js | 2 - src/models/popups/Popup.js | 2 +- src/services/electron/FileService.js | 95 +++--- src/services/electron/Injectable.js | 5 +- src/services/electron/SecurePasswords.js | 29 ++ src/services/electron/StorageService.js | 2 + src/services/utility/BackupService.js | 39 --- src/services/utility/WebHashChecker.js | 46 +++ src/store/actions.js | 1 - src/styles/styles.scss | 2 + src/util/ElectronHelpers.js | 56 +--- src/util/WalletTalk.js | 3 + src/views/Login.vue | 11 +- src/views/PopOut.vue | 6 +- src/views/Scatter.vue | 27 +- test/README.md | 33 -- test/babel.js | 4 - test/helpers.js | 17 -- test/mocks.js | 62 ---- test/unit/accounts.spec.js | 158 ---------- test/unit/balances.spec.js | 50 ---- test/unit/bitcoin.spec.js | 51 ---- test/unit/locale.spec.js | 34 --- test/unit/networks.spec.js | 162 ---------- test/unit/webhash.spec.js | 15 + yarn.lock | 190 ++++-------- 43 files changed, 1042 insertions(+), 1027 deletions(-) delete mode 100644 .babelrc create mode 100644 babel.config.js rename {src/models => electron}/hardware/LedgerWallet.js (73%) create mode 100644 electron/services/hardware.js create mode 100644 src/components/popins/fullscreen/ConfirmPassword.vue create mode 100644 src/components/popins/fullscreen/ExportPrivateKey.vue create mode 100644 src/services/electron/SecurePasswords.js create mode 100644 src/services/utility/WebHashChecker.js delete mode 100644 test/README.md delete mode 100644 test/babel.js delete mode 100644 test/helpers.js delete mode 100644 test/mocks.js delete mode 100644 test/unit/accounts.spec.js delete mode 100644 test/unit/balances.spec.js delete mode 100644 test/unit/bitcoin.spec.js delete mode 100644 test/unit/locale.spec.js delete mode 100644 test/unit/networks.spec.js create mode 100644 test/unit/webhash.spec.js diff --git a/.babelrc b/.babelrc deleted file mode 100644 index b5f2b880..00000000 --- a/.babelrc +++ /dev/null @@ -1,18 +0,0 @@ -{ - "presets": [ - [ - "@babel/preset-env", - { - "modules": false - } - ] - ], - "plugins": [ - "@babel/plugin-syntax-dynamic-import", - ["@babel/plugin-transform-runtime", - { - "regenerator": true - } - ] - ] -} \ No newline at end of file diff --git a/babel.config.js b/babel.config.js new file mode 100644 index 00000000..970b0e59 --- /dev/null +++ b/babel.config.js @@ -0,0 +1,28 @@ +module.exports = { + 'env': { + 'prod':{ + "presets": [ + [ + "@babel/preset-env", + { + "modules": false + } + ] + ], + "plugins": [ + "@babel/plugin-syntax-dynamic-import", + ["@babel/plugin-transform-runtime", + { + "regenerator": true + } + ] + ] + }, + 'test': { + "presets": ["@babel/preset-env"], + "plugins": [ + ["@babel/transform-runtime"] + ], + } + }, +} diff --git a/build/webpack.config.base.js b/build/webpack.config.base.js index b2963e1c..5e5142da 100644 --- a/build/webpack.config.base.js +++ b/build/webpack.config.base.js @@ -86,5 +86,9 @@ module.exports = { to: utils.resolve('dist/static'), toType: 'dir' }]), - ] + ], + // module not found fs error revealer + // node: { + // fs: 'empty' + // } } \ No newline at end of file diff --git a/electron/app.js b/electron/app.js index d6d20d74..c20ed261 100644 --- a/electron/app.js +++ b/electron/app.js @@ -157,8 +157,6 @@ app.on('will-finish-launching', () => { const Transport = require('@ledgerhq/hw-transport-node-hid'); - - global.appShared = { Transport, QuitWatcher:null, @@ -209,7 +207,7 @@ const wallet = require('./services/wallet'); // FORWARDING FROM INJECTED DOM global.scatterMessage = async (data) => { - console.log('got data',data); + console.log('ipc data', data.service, data.method); if(data.service === 'popout' && data.method === 'response') { mainWindow.webContents.send('popoutResponse', data); return null; } if(data.method === 'getScatter') return {data:wallet.getScatter(), id:data.id}; @@ -220,6 +218,10 @@ global.scatterMessage = async (data) => { if(data.service === 'sign' && data.method === 'sign') return {data:await wallet.sign(...data.data), id:data.id}; if(data.method === 'setScatter') return {data:await wallet.updateScatter(...data.data), id:data.id}; + // Hardware wallets + if(data.service === 'hardware' && data.method === 'types') return {data:await wallet.hardwareTypes, id:data.id}; + if(data.service === 'hardware' && data.method === 'keys') return {data:await wallet.getHardwareKeys(data.data), id:data.id}; + mainWindow.webContents.send('scatter', data); } diff --git a/src/models/hardware/LedgerWallet.js b/electron/hardware/LedgerWallet.js similarity index 73% rename from src/models/hardware/LedgerWallet.js rename to electron/hardware/LedgerWallet.js index bcec4384..8567becb 100644 --- a/src/models/hardware/LedgerWallet.js +++ b/electron/hardware/LedgerWallet.js @@ -1,42 +1,50 @@ -import bippath from 'bip32-path'; -import {Blockchains} from '@walletpack/core/models/Blockchains'; -import PopupService from '../../services/utility/PopupService'; -import {Popup} from '../popups/Popup'; +const bippath = require('bip32-path'); +const {Blockchains} = require('@walletpack/core/models/Blockchains'); -const fcbuffer = require('fcbuffer'); const asn1 = require('asn1-ber'); -import ecc from 'eosjs-ecc'; -import { Serialize } from 'eosjs'; +const ecc = require('eosjs-ecc'); +const { Serialize } = require('eosjs'); const EthTx = require('ethereumjs-tx') -import Eth from "@ledgerhq/hw-app-eth"; -import {EXT_WALLET_TYPES} from "@walletpack/core/models/hardware/ExternalWallet"; -// import {encoderOptions, eosjsUtil} from '../../plugins/defaults/eos' -import {store} from "../../store/store"; +const Eth = require("@ledgerhq/hw-app-eth"); -let encoderOptions, eosjsUtil; +const Transport = require('@ledgerhq/hw-transport-node-hid').default; +let transport, openedTransport; +class LedgerTransport { + static setup(){ + transport = Transport.listen({next:({type, device}) => this[type](device)}); + } + + static async add(device){ + const {path} = device; + openedTransport = await Transport.open(path); + } + + static async remove(device){ + openedTransport = null; + transport = Transport.listen({next:({type, device}) => this[type](device)}); + } +} + +const getTransport = () => openedTransport; -const throwErr = () => PopupService.push(Popup.prompt( - 'No Hardware Available', - 'You either need to plug in your Ledger, or select the appropriate App.' -)); -export const LEDGER_PATHS = { + + + +const LEDGER_PATHS = { [Blockchains.EOSIO]:(index = 0) => `44'/194'/0'/0/${index}`, [Blockchains.ETH]:(index = 0) => `44'/60'/0'/0/${index}`, } -const getTransport = () => { - if(!store.state.hardware.hasOwnProperty('LEDGER')) return null; - return store.state.hardware['LEDGER']; -} -export default class LedgerWallet { - constructor(blockchain){ - this.blockchain = blockchain; - this.api = null; +let encoderOptions, eosjsUtil; +class LedgerWallet { + + static setup(){ + LedgerTransport.setup(); const EosPlugin = require('@walletpack/eosio'); if(EosPlugin){ @@ -45,11 +53,16 @@ export default class LedgerWallet { } } + constructor(blockchain){ + this.blockchain = blockchain; + this.api = null; + } + static typeToInterface(blockchain){ return new LedgerWallet(blockchain); }; - availableBlockchains(){ + static availableBlockchains(){ return [Blockchains.EOSIO, Blockchains.ETH]; } @@ -60,6 +73,7 @@ export default class LedgerWallet { this.sign = this.api.signTransaction; this.canConnect = this.api.getAppConfiguration; this.setAddressIndex = this.api.setAddressIndex; + return true; } close(){ @@ -69,6 +83,7 @@ export default class LedgerWallet { delete this.sign; delete this.canConnect; delete this.setAddressIndex; + return true; } } @@ -115,10 +130,10 @@ class LedgerAPI { prefix.addressIndex = index; } - async getAddress(delta = 0){ + async getAddress(index){ if(!getTransport()) return; const prefix = this.api ? this.api : this; - return prefix[`getAddress`+this.blockchain](delta); + return prefix[`getAddress`+this.blockchain](index); } @@ -150,8 +165,8 @@ class LedgerAPI { /* GET ADDRESS */ /*************************************************/ - [`getAddress`+Blockchains.EOSIO](delta = 0, boolChaincode = false){ - const path = LEDGER_PATHS[this.blockchain]((parseInt(this.addressIndex) + parseInt(delta)).toString()); + [`getAddress`+Blockchains.EOSIO](index, boolChaincode = false){ + const path = LEDGER_PATHS[this.blockchain](index.toString()); const paths = bippath.fromString(path).toPathArray(); let buffer = new Buffer(1 + paths.length * 4); buffer[0] = paths.length; @@ -166,12 +181,15 @@ class LedgerAPI { result.chainCode = response.slice(1 + publicKeyLength + 1 + addressLength, 1 + publicKeyLength + 1 + addressLength + 32).toString("hex"); } return result.address; + }).catch(err => { + console.error('Ledger address error', err); + return null; }); } - [`getAddress`+Blockchains.ETH](delta = 0){ + [`getAddress`+Blockchains.ETH](index){ return new Promise(async (resolve, reject) => { - const path = LEDGER_PATHS[this.blockchain](parseInt(this.addressIndex) + parseInt(delta)); + const path = LEDGER_PATHS[this.blockchain](index); const eth = new Eth(getTransport()); eth.getAddress(path, false) .then(response => { @@ -184,75 +202,6 @@ class LedgerAPI { - - - /*************************************************/ - /* GET PUBLIC KEY */ - /*************************************************/ - - [`getPublicKey`+Blockchains.EOSIO](){ - return new Promise((resolve, reject) => { - setTimeout(() => { - const path = LEDGER_PATHS[this.blockchain](this.addressIndex); - const paths = bippath.fromString(path).toPathArray(); - const buffer = Buffer.alloc(1 + paths.length * 4); - buffer[0] = paths.length; - paths.forEach((element, index) => buffer.writeUInt32BE(element, 1 + 4 * index)); - - const popup = Popup.checkHardwareWalletScreen(); - PopupService.push(popup); - - return getTransport() - .send( - LEDGER_CODES.CLA, - LEDGER_CODES.PK, - LEDGER_CODES.YES, // Trigger on-screen approval - LEDGER_CODES.NO, // chaincode - buffer - ) - .then(response => { - PopupService.remove(popup); - const result = {}; - const publicKeyLength = response[0]; - const addressLength = response[1 + publicKeyLength]; - - resolve(response - .slice( - 1 + publicKeyLength + 1, - 1 + publicKeyLength + 1 + addressLength - ) - .toString("ascii")); - }).catch(err => { - PopupService.remove(popup); - reject(err); - }) - }, 1); - }) - } - - [`getPublicKey`+Blockchains.ETH](){ - return new Promise(async (resolve, reject) => { - const popup = Popup.checkHardwareWalletScreen(); - PopupService.push(popup); - const path = LEDGER_PATHS[this.blockchain](this.addressIndex); - const eth = new Eth(getTransport()); - eth.getAddress(path, true) - .then(response => { - PopupService.remove(popup); - resolve(response.address); - }).catch(err => { - PopupService.remove(popup); - reject(err); - }); - }) - } - - - - - - - /*************************************************/ /* SIGN TRANSACTION */ /*************************************************/ @@ -269,7 +218,8 @@ class LedgerAPI { rawTx = serializeEosjs(network, transaction); } catch(e){ console.error('e', e); - PopupService.push(Popup.prompt('Ledger Action Not Supported', 'Looks like this action isn\'t supported by the Ledger App')); + // TODO: NOTIFICATIONS + // PopupService.push(Popup.prompt('Ledger Action Not Supported', 'Looks like this action isn\'t supported by the Ledger App')); return null; } @@ -288,22 +238,23 @@ class LedgerAPI { offset += chunkSize; } - const popup = Popup.checkHardwareWalletScreen(); - PopupService.push(popup); + // TODO: NOTIFICATIONS + // const popup = Popup.checkHardwareWalletScreen(); + // PopupService.push(popup); return foreach(toSend, (data, i) => getTransport() .send(LEDGER_CODES.CLA, LEDGER_CODES.SIGN, i === 0 ? LEDGER_CODES.FIRST : LEDGER_CODES.MORE, 0x00, data) .then(apduResponse => response = apduResponse) ).then(() => { - PopupService.remove(popup); + // PopupService.remove(popup); const v = response.slice(0, 1).toString("hex"); const r = response.slice(1, 1 + 32).toString("hex"); const s = response.slice(1 + 32, 1 + 32 + 32).toString("hex"); return ecc.Signature.fromHex(v+r+s).toString(); }).catch(err => { console.error('err', err); - PopupService.remove(popup); + // PopupService.remove(popup); return null; }) } @@ -313,19 +264,19 @@ class LedgerAPI { const path = LEDGER_PATHS[this.blockchain](this.addressIndex); const eth = new Eth(getTransport()); const popup = Popup.checkHardwareWalletScreen(); - PopupService.push(popup); + // PopupService.push(popup); const chainIdHex = '0x'+(network.chainId.length === 1 ? '0'+ network.chainId : network.chainId).toString(); const baseTransaction = Object.assign(transaction, {chainId:parseInt(network.chainId), r:'0x00', s:'0x00', v:chainIdHex}); const rawTxHex = (new EthTx(baseTransaction)).serialize().toString('hex'); return eth.signTransaction(path, rawTxHex).then(res => { - PopupService.remove(popup); + // PopupService.remove(popup); const r = '0x' + res.r; const s = '0x' + res.s; const v = '0x' + res.v; const signed = Object.assign(baseTransaction, {r,s,v}); return '0x'+(new EthTx(signed)).serialize().toString('hex'); }).catch(err => { - PopupService.remove(popup); + // PopupService.remove(popup); return null; }) } @@ -372,7 +323,7 @@ class LedgerAPI { -export const serializeEosjs = (network, transaction) => { +const serializeEosjs = (network, transaction) => { const types = {}; eosjsUtil.abiTypes.forEach((value, key) => types[key] = value); @@ -462,4 +413,14 @@ const serialize = (chainId, transaction, types) => { return writer.buffer; } -const encode = (writer, buffer) => writer.writeBuffer(buffer, asn1.Ber.OctetString); \ No newline at end of file +const encode = (writer, buffer) => writer.writeBuffer(buffer, asn1.Ber.OctetString); + + + + + + + + + +module.exports = LedgerWallet; \ No newline at end of file diff --git a/electron/services/hardware.js b/electron/services/hardware.js new file mode 100644 index 00000000..0038e0c6 --- /dev/null +++ b/electron/services/hardware.js @@ -0,0 +1,93 @@ +// const Transport = remote.getGlobal('appShared').Transport.default; +// import * as Actions from "../../store/constants"; +// import {EXT_WALLET_TYPES} from "../../models/hardware/ExternalWallet"; +// import PopupService from "../utility/PopupService"; +// import {Popup} from "../../models/popups/Popup"; +// import KeyPairService from "./KeyPairService"; +// import StoreService from "../utility/StoreService"; +// +// const isLedgerConnected = () => StoreService.get().state.hardware.hasOwnProperty(EXT_WALLET_TYPES.LEDGER); +// const hardwareKeypairs = (type) => StoreService.get().getters.keypairs.filter(x => !!x.external && x.external.type === type); +// +// let transport = null; +// +// class HardwareService { +// +// static async openConnections(onlyIfDisconnected = false){ +// return this.checkLedgerConnection(onlyIfDisconnected); +// } +// +// static async checkLedgerConnection(onlyIfDisconnected = false){ +// if(!onlyIfDisconnected || (!isLedgerConnected() && !transport)){ +// return new Promise(resolve => { +// transport = Transport.listen({next:({type, device}) => this[type+'Ledger'](device)}); +// setTimeout(() => { +// resolve(true); +// }, 1000); +// }) +// } else { +// return true; +// } +// } +// +// static async addLedger(device){ +// const {path} = device; +// const openedTransport = await Transport.open(path); +// await StoreService.get().dispatch(Actions.SET_HARDWARE, {name:EXT_WALLET_TYPES.LEDGER, transport:openedTransport}); +// +// hardwareKeypairs(EXT_WALLET_TYPES.LEDGER).map(keypair => { +// keypair.external.interface.open(); +// }) +// } +// +// static async removeLedger(device){ +// if(isLedgerConnected()){ +// if(StoreService.get().state.hardware.hasOwnProperty(EXT_WALLET_TYPES.LEDGER)) +// await StoreService.get().state.hardware[EXT_WALLET_TYPES.LEDGER].disconnect(); +// +// await StoreService.get().dispatch(Actions.REMOVE_HARDWARE, EXT_WALLET_TYPES.LEDGER); +// transport = Transport.listen({next:({type, device}) => this[type](device)}); +// } +// } +// +// +// +// +// +// +// +// +// +// +// static async checkHardware(account){ +// await this.openConnections(true); +// +// const popup = canConnect => new Promise(resolve => { +// PopupService.push(Popup.prompt('Hardware Error', canConnect, async opened => { +// if(!opened) return resolve(false); +// account.keypair().resetExternal(); +// await HardwareService.openConnections(); +// resolve(this.checkHardware(account)); +// })) +// }); +// +// if(KeyPairService.isHardware(account.publicKey)){ +// account.keypair().external.interface.open(); +// const canConnect = await account.keypair().external.interface.canConnect(); +// if(canConnect !== true) return await popup(canConnect); +// return true; +// } else return true; +// } +// +// static async sign(account, payload){ +// const canConnect = await this.checkHardware(account); +// if(!canConnect) return false; +// +// +// const keypair = KeyPairService.getKeyPairFromPublicKey(account.publicKey); +// if(typeof keypair.external.interface.setAddressIndex === 'undefined') keypair.external.interface.open(); +// keypair.external.interface.setAddressIndex(keypair.external.addressIndex); +// return keypair.external.interface.sign(account.publicKey, payload, payload.abi, account.network()); +// } +// +// } \ No newline at end of file diff --git a/electron/services/storage.js b/electron/services/storage.js index c2fb4ef3..bf74a2ee 100644 --- a/electron/services/storage.js +++ b/electron/services/storage.js @@ -22,7 +22,6 @@ const scatterIntermedStorage = () => getStore(SCATTER_INTERMED_NAME); const abiStorage = () => getStore(ABIS_NAME); const electron = require('electron'); -console.log('electron', electron); const {app} = electron; const fs = require('fs'); @@ -81,7 +80,6 @@ const removeScatter = () => { } const getSalt = () => { - console.log('getting salt') return scatterStorage().get('salt') || 'SALT_ME'; } const setSalt = (salt) => scatterStorage().set('salt', salt); diff --git a/electron/services/wallet.js b/electron/services/wallet.js index c1a5e9e2..e5e1485f 100644 --- a/electron/services/wallet.js +++ b/electron/services/wallet.js @@ -1,3 +1,5 @@ + + const LowLevelWindowService = require("./windows"); const {ipcMain} = require("electron"); @@ -8,8 +10,11 @@ const AES = require("aes-oop").default; const storage = require('./storage') const path = require('path') const Scatter = require('@walletpack/core/models/Scatter').default; -const PluginRepository = require('@walletpack/core/plugins/PluginRepository').default; const Error = require('@walletpack/core/models/errors/Error').default; +const IdGenerator = require('@walletpack/core/util/IdGenerator').default; +const Hasher = require('@walletpack/core/util/Hasher').default; +const Keypair = require('@walletpack/core/models/Keypair').default; +const Crypto = require('@walletpack/core/util/Crypto').default; const EOSIO = require('@walletpack/eosio').default; @@ -33,8 +38,8 @@ salt = storage.getSalt(); const setScatter = (_s) => scatter = JSON.parse(JSON.stringify(_s)); const getScatter = () => scatter ? JSON.parse(JSON.stringify(scatter)) : null; +const isEncrypted = x => x.toString().indexOf('"iv":') > -1 const updateScatter = async (_s) => { - const isEncrypted = x => x.toString().indexOf('"iv":') > -1 _s.keychain.keypairs.map(x => { if(!isEncrypted(x.privateKey)){ @@ -61,6 +66,41 @@ const updateScatter = async (_s) => { return getScatter(); } +const verifyPassword = async password => { + const hash = await passwordToSeed(password); + return seed === hash; +} + +const changePassword = async (newPassword) => { + const oldSalt = storage.getSalt(); + const oldSeed = seed; + + const newSalt = Hasher.unsaltedQuickHash(IdGenerator.text(32)); + await storage.setSalt(newSalt); + + const newSeed = await passwordToSeed(newPassword); + seed = newSeed; + + const clone = JSON.parse(JSON.stringify(scatter)); + clone.keychain.keypairs.map(keypair => { + keypair.privateKey = AES.decrypt(keypair.privateKey, oldSeed); + keypair.privateKey = AES.encrypt(keypair.privateKey, newSeed); + }); + clone.keychain.identities.map(id => { + id.privateKey = AES.decrypt(id.privateKey, oldSeed); + id.privateKey = AES.encrypt(id.privateKey, newSeed); + }); + + await updateScatter(clone); + resolve(true); + + // await StoreService.get().dispatch(Actions.SET_SCATTER, scatter); + // await StorageService.swapHistory(StoreService.get().state.history); + // await StorageService.setTranslation(Locale.fromJson(StoreService.get().state.language.json)); + // StoreService.get().dispatch(Actions.LOAD_HISTORY); + // StoreService.get().dispatch(UIActions.LOAD_LANGUAGE); +} + @@ -86,7 +126,24 @@ const passwordToSeed = async password => { } +const reloading = () => { + if(seed) seed = null; + if(scatter) scatter = storage.getScatter(); +}; + +const getPrivateKey = async (keypairId, blockchain) => { + console.log('getting private key') + let keypair = scatter.keychain.keypairs.find(x => x.id === keypairId); + console.log('keypair', keypair); + if(!keypair) return; + keypair = Keypair.fromJson(keypair); + + const encryptedKey = JSON.parse(JSON.stringify(keypair.privateKey)); + const decryptedKey = AES.decrypt(encryptedKey, seed); + + return plugins[blockchain].bufferToHexPrivate(decryptedKey); +} const unlock = async (password, isNew = false) => { try { @@ -115,13 +172,13 @@ const sign = (network, publicKey, payload, arbitrary = false, isHash = false) => try { const plugin = plugins[network.blockchain]; - console.log('plugin', plugin, network.blockchain); if(!plugin) return false; const keypair = scatter.keychain.keypairs.find(x => x.publicKeys.find(k => k.key === publicKey)) - console.log('keypair', !!keypair); if(!keypair) return Error.signatureError('no_keypair', 'This keypair could not be found'); + if(keypair.external) return signWithHardware(keypair, network, publicKey, payload, arbitrary, isHash); + let privateKey = AES.decrypt(keypair.privateKey, seed); if(!privateKey) return Error.signatureError('sign_err', 'Could not decode private key'); if(typeof privateKey === 'object' && privateKey.hasOwnProperty('data')) privateKey = privateKey.data; @@ -142,10 +199,66 @@ ipcMain.on('load', (e) => { + + +const LedgerWallet = require("../hardware/LedgerWallet"); + +const hardwareTypes = [ + {name:'Ledger', blockchains:LedgerWallet.availableBlockchains()}, +] + +const getHardwareKeys = async ({external, indexes}) => { + return new Promise(async resolve => { + const {blockchain} = external; + + await LedgerWallet.setup(); + const ledger = new LedgerWallet(blockchain); + await ledger.open(); + + let result = []; + await new Promise(r => setTimeout(async () => { + if(!ledger.canConnect()) return {error:`cant_connect`}; + + indexes = indexes.sort(); + + for(let i = 0; i < indexes.length; i++){ + const key = await ledger.getAddress(indexes[i]); + result.push({key, index:indexes[i]}); + } + + return r(true); + }, 100)); + + return resolve(result); + }) +}; + +const signWithHardware = async (keypair, network, publicKey, payload, arbitrary = false, isHash = false) => { + const {blockchain} = network; + + await LedgerWallet.setup(); + const ledger = new LedgerWallet(blockchain); + await ledger.open(); + + return new Promise(r => setTimeout(async () => { + // TODO: fix me + // if(!ledger.canConnect()) return {error:`cant_connect`}; + ledger.setAddressIndex(keypair.external.addressIndex); + r(await ledger.sign(publicKey, payload, payload.abi, network)); + }, 100)); +} + + module.exports = { updateScatter, setScatter, getScatter, sign, + getPrivateKey, + reloading, unlock, + verifyPassword, + changePassword, + hardwareTypes, + getHardwareKeys } \ No newline at end of file diff --git a/index.html b/index.html index 2467283e..09ac78c8 100644 --- a/index.html +++ b/index.html @@ -10,11 +10,24 @@ Scatter + +
+
diff --git a/package.json b/package.json index 0dfe6cc2..d554af51 100644 --- a/package.json +++ b/package.json @@ -17,9 +17,9 @@ "release-mac": "electron-builder --mac", "release-win": "electron-builder --win", "release-web": "node package_web.js", - "start": "npm run dev", - "testfile": "mocha --require test/babel --exit --timeout 1000000", - "test": "npm run testfile \"test/**/*.spec.js\" --timeout 1000000" + "start": "cross-env BABEL_ENV=prod npm run dev", + "test": "cross-env BABEL_ENV=test mocha --require @babel/register \"test/**/*.spec.js\" --exit --timeout 1000000", + "testfile": "cross-env BABEL_ENV=test mocha --require @babel/register --exit --timeout 1000000" }, "dependencies": { "@babel/runtime": "^7.5.4", @@ -40,7 +40,6 @@ "electron-store": "^3.2.0", "eosjs": "^20.0.0", "ethereumjs-tx": "^2.1.0", - "fcbuffer": "^2.2.2", "isomorphic-ws": "^4.0.1", "json-formatter-js": "^2.2.1", "mini-css-extract-plugin": "^0.8.0", @@ -56,6 +55,7 @@ "@babel/plugin-syntax-dynamic-import": "^7.2.0", "@babel/plugin-transform-runtime": "^7.5.0", "@babel/preset-env": "^7.5.4", + "@babel/register": "^7.6.0", "autoprefixer": "^9.5.1", "babel-loader": "^8.0.6", "chai": "^4.2.0", @@ -76,10 +76,10 @@ "friendly-errors-webpack-plugin": "^1.6.1", "html-webpack-plugin": "^3.2.0", "inject-loader": "^3.0.0", + "isomorphic-fetch": "^2.2.1", "jsdom": "^13.0.0", "jsdom-global": "^3.0.2", - "mocha": "^6.1.4", - "mocha-jsdom": "^2.0.0", + "mocha": "^6.2.0", "node-sass": "^4.12.0", "ora": "^1.2.0", "postcss-import": "^11.0.0", diff --git a/preload.js b/preload.js index 5e5d2a4e..14b4fd6a 100644 --- a/preload.js +++ b/preload.js @@ -1,13 +1,17 @@ const { ipcRenderer: ipc, remote } = require('electron'); let send = remote.getGlobal('scatterMessage'); let sendLog = remote.getGlobal('scatterLog'); -console.log('preload', location); -ipc.on('scatter', (event, data) => window.ScatterWallet.received(data)); -ipc.on('sockets', (event, data) => window.ScatterWallet.sockets(data)); -ipc.on('popout', (event, data) => window.ScatterWallet.popout(data)); -window.ScatterWallet = { send, windowId:remote.getCurrentWindow().id }; +if(!window.wallet) window.wallet = {}; +ipc.on('scatter', (event, data) => window.wallet.received(data)); +ipc.on('sockets', (event, data) => window.wallet.sockets(data)); +ipc.on('popout', (event, data) => window.wallet.popout(data)); + +window.wallet.send = send; +window.wallet.windowId = remote.getCurrentWindow().id; + +console.log('preloaded', window.wallet); const log = console.log; console.log = (...params) => { diff --git a/src/components/MenuBar.vue b/src/components/MenuBar.vue index 362b8dd5..33305869 100644 --- a/src/components/MenuBar.vue +++ b/src/components/MenuBar.vue @@ -5,6 +5,13 @@
+ +
+
+
+
+
+
@@ -32,6 +39,8 @@ + + \ No newline at end of file diff --git a/src/components/popins/fullscreen/ExportPrivateKey.vue b/src/components/popins/fullscreen/ExportPrivateKey.vue new file mode 100644 index 00000000..96e15516 --- /dev/null +++ b/src/components/popins/fullscreen/ExportPrivateKey.vue @@ -0,0 +1,281 @@ + + + + + \ No newline at end of file diff --git a/src/components/popins/fullscreen/ImportBackup.vue b/src/components/popins/fullscreen/ImportBackup.vue index abb21af0..57b2593b 100644 --- a/src/components/popins/fullscreen/ImportBackup.vue +++ b/src/components/popins/fullscreen/ImportBackup.vue @@ -34,10 +34,11 @@ import AES from 'aes-oop'; import Crypto from "@walletpack/core/util/Crypto"; import * as UIActions from "../../../store/ui_actions"; + import * as FileService from "../../../services/electron/FileService"; const {getFileLocation} = require('../../../services/electron/FileService'); - const ipcFaF = require('../../../util/ElectronHelpers').ipcFaF; const reload = require('../../../util/ElectronHelpers').default.reload; - // const fs = window.require('fs'); + const fs = window.require('fs'); + const {Wallet} = window.require('electron').remote.getGlobal('appShared'); export default { components: {LoginButton}, @@ -58,38 +59,48 @@ // this.setWorkingScreen(false); this.restoringBackup = false; } + if(this.restoringBackup) return; this.restoringBackup = true; + const possibleFile = getFileLocation(['json', 'txt']); if(!possibleFile) return unrestore(); const file = possibleFile[0]; if(!file) return unrestore(); + + console.log('file', file); + + + const importDesktopBackup = async (data, password) => { const [obj, salt] = data.split('|SLT|'); if(!obj || !salt) { unrestore(); - return PopupService.push(Popup.snackbar(this.locale(this.langKeys.SNACKBARS.AUTH.ErrorParsingBackup))); + return PopupService.push(Popup.snackbar(`Error parsing backup file`)); } const [_, seed] = await Mnemonic.generateMnemonic(password, salt); const decrypted = AES.decrypt(obj, seed); if(typeof decrypted === 'object' && decrypted.hasOwnProperty('keychain')){ decrypted.keychain = AES.decrypt(decrypted.keychain, seed); decrypted.settings.backupLocation = ''; - StorageService.setSalt(salt); - await this[UIActions.SET_SEED](password); - await this[Actions.SET_SCATTER](Scatter.fromJson(decrypted)); - ipcFaF('key', null); + + await StorageService.setSalt(salt); + await Wallet.unlock(password, true); + await Wallet.updateScatter(decrypted); reload() } else { unrestore(); - return PopupService.push(Popup.snackbar(this.locale(this.langKeys.SNACKBARS.AUTH.ErrorDecryptingBackup))); + return PopupService.push(Popup.snackbar(`Error decrypting backup file`)); } }; + + + const importExtensionBackup = async (data, password) => { const [obj, salt] = data.split('|SSLT|'); if(!obj || !salt) { unrestore(); - return PopupService.push(Popup.snackbar(this.locale(this.langKeys.SNACKBARS.AUTH.ErrorParsingBackup))); + return PopupService.push(Popup.snackbar(`Error parsing backup file`)); } const [_, seed] = await Mnemonic.generateMnemonic(password, salt); const decrypted = AES.decrypt(obj, seed); @@ -116,36 +127,38 @@ await Promise.all(keypairs.map(keypair => { return AccountService.importAllAccounts(keypair); })); - ipcFaF('key', null); reload() } else { unrestore(); - return PopupService.push(Popup.snackbar(this.locale(this.langKeys.SNACKBARS.AUTH.ErrorDecryptingBackup))); + return PopupService.push(Popup.snackbar(`Error decrypting backup file`)); } }; - // TODO: Fix for both web and desktop - // fs.readFile(file, 'utf-8', (err, data) => { - // const fileExtension = file.split('.')[file.split('.').length-1]; - // if(err) { - // unrestore(); - // return PopupService.push(Popup.snackbar(this.locale(this.langKeys.SNACKBARS.AUTH.CantReadBackup))); - // } - // PopupService.push(Popup.verifyPassword(async password => { - // if(!password || !password.length) return unrestore(); - // this.setWorkingScreen(true); - // try { - // switch(fileExtension){ - // case 'json': return await importDesktopBackup(data, password); - // case 'txt': return await importExtensionBackup(data, password); - // } - // } catch(e){ - // console.error('e',e); - // unrestore(); - // return PopupService.push(Popup.snackbar(this.locale(this.langKeys.SNACKBARS.AUTH.ErrorDecryptingBackup))); - // } - // }, true)) - // }); + FileService.openFile(file).then(data => { + if(!data){ + + unrestore(); + return PopupService.push(Popup.snackbar(`Can't read backup file`)); + } + + PopupService.push(Popup.verifyPassword(async password => { + console.log('pass?', password); + if(!password || !password.length) return unrestore(); + // TODO: + // this.setWorkingScreen(true); + try { + const fileExtension = file.split('.')[file.split('.').length-1]; + switch(fileExtension){ + case 'json': return await importDesktopBackup(data, password); + case 'txt': return await importExtensionBackup(data, password); + } + } catch(e){ + console.error('e',e); + unrestore(); + return PopupService.push(Popup.snackbar(`Error decrypting backup file`)); + } + }, true)) + }) }, ...mapActions([ diff --git a/src/main.js b/src/main.js index 7b339ede..6f70fa5a 100644 --- a/src/main.js +++ b/src/main.js @@ -41,8 +41,6 @@ document.onmousedown= e => { class Main { constructor(){ - console.log('This is desktop wrapper'); - const isPopOut = location.hash.replace("#/", '') === 'popout'; const components = [ diff --git a/src/models/popups/Popup.js b/src/models/popups/Popup.js index 33f8cdc0..8a25e046 100644 --- a/src/models/popups/Popup.js +++ b/src/models/popups/Popup.js @@ -224,7 +224,7 @@ export class Popup { } static exportPrivateKey(keypair, callback){ - return new Popup(PopupDisplayTypes.POP_IN, new PopupData(PopupTypes.EXPORT_PRIVATE_KEY, {keypair:keypair.clone()}, callback)) + return new Popup(PopupDisplayTypes.POP_IN, new PopupData(PopupTypes.EXPORT_PRIVATE_KEY, {keypair:keypair}, callback)) } static setDisplayToken(callback = () => {}){ diff --git a/src/services/electron/FileService.js b/src/services/electron/FileService.js index 6dde94c1..170440ea 100644 --- a/src/services/electron/FileService.js +++ b/src/services/electron/FileService.js @@ -1,4 +1,6 @@ import {remote} from '../../util/ElectronHelpers'; +import PopupService from "../utility/PopupService"; +import {Popup} from "../../models/popups/Popup"; const fs = window.require('fs'); export const getFileLocation = (extensions) => remote.dialog.showOpenDialog({ filters: [ { name: 'only', extensions } ] }); @@ -17,9 +19,26 @@ export const saveFile = (path, name, data, encoding = 'utf-8') => { }) }; -export const uploadAvatar = () => { - // TODO: FIX! - return true; +export const openFile = (path, encoding = 'utf-8') => { + return new Promise(resolve => { + try { + fs.readFile(path, encoding, (err, data) => { + if(err) { + console.error('err', err); + resolve(null); + } + + resolve(data); + }); + } + catch(e) { + console.error('Error opening file', e); + resolve(null); + } + }) +}; + +export const uploadAvatar = async () => { // //TODO: I'm not sure this is the best way to go about this. // /*** // * It's possible that this could inflate the saved json and backups significantly. @@ -27,39 +46,39 @@ export const uploadAvatar = () => { // * Need to give this a think. // */ // - // let filepath = await getFileLocation(['jpg', 'png', 'jpeg']); - // if(!filepath || !filepath.length) return; - // filepath = filepath[0]; - // let ext = filepath.split('.'); - // ext = ext[ext.length-1]; - // - // const base64 = fs.readFileSync(filepath, { encoding: 'base64' }); - // if(!base64) return PopupService.push(Popup.snackbar("Error converting image file.")); - // - // // Resizing to 350x350 MAX (ratio preserved) - // // ------------------------------------------- - // const canvas = document.createElement("canvas"); - // const ctx = canvas.getContext("2d"); - // const image = new Image(); - // - // image.onload = e => { - // const calculateAspectRatioFit = () => { - // const ratio = Math.min(350 / image.width, 350 / image.height); - // return { width: Math.round(image.width*ratio), height: Math.round(image.height*ratio) }; - // } - // - // canvas.height = calculateAspectRatioFit().height; - // canvas.width = calculateAspectRatioFit().width; - // - // ctx.drawImage(image, 0, 0, image.width, image.height, 0, 0, calculateAspectRatioFit().width, calculateAspectRatioFit().height); - // const resized = new Image(); - // resized.src = canvas.toDataURL(`image/${ext}`); - // - // const scatter = this.scatter.clone(); - // scatter.keychain.avatars[this.identity.id] = resized.src; - // this[Actions.SET_SCATTER](scatter); - // }; - // - // image.src = `data:image/${ext};base64, ${base64}`; - // // ------------------------------------------- + let filepath = await getFileLocation(['jpg', 'png', 'jpeg']); + if(!filepath || !filepath.length) return; + filepath = filepath[0]; + let ext = filepath.split('.'); + ext = ext[ext.length-1]; + + const base64 = fs.readFileSync(filepath, { encoding: 'base64' }); + if(!base64) return PopupService.push(Popup.snackbar("Error converting image file.")); + + // Resizing to 350x350 MAX (ratio preserved) + // ------------------------------------------- + const canvas = document.createElement("canvas"); + const ctx = canvas.getContext("2d"); + const image = new Image(); + + image.onload = e => { + const calculateAspectRatioFit = () => { + const ratio = Math.min(350 / image.width, 350 / image.height); + return { width: Math.round(image.width*ratio), height: Math.round(image.height*ratio) }; + } + + canvas.height = calculateAspectRatioFit().height; + canvas.width = calculateAspectRatioFit().width; + + ctx.drawImage(image, 0, 0, image.width, image.height, 0, 0, calculateAspectRatioFit().width, calculateAspectRatioFit().height); + const resized = new Image(); + resized.src = canvas.toDataURL(`image/${ext}`); + + const scatter = this.scatter.clone(); + scatter.keychain.avatars[this.identity.id] = resized.src; + this[Actions.SET_SCATTER](scatter); + }; + + image.src = `data:image/${ext};base64, ${base64}`; + // ------------------------------------------- } \ No newline at end of file diff --git a/src/services/electron/Injectable.js b/src/services/electron/Injectable.js index 7c2af5e1..b976fa9b 100644 --- a/src/services/electron/Injectable.js +++ b/src/services/electron/Injectable.js @@ -6,8 +6,9 @@ export default class Injectable { return ElectronHelpers.getDefaultPath(); } - static async openLink(link){ - return ElectronHelpers.openLinkInBrowser(link); + static async openLink(link, filepath = false){ + console.log('opening link') + return ElectronHelpers.openLinkInBrowser(link, filepath); } static async copy(text){ diff --git a/src/services/electron/SecurePasswords.js b/src/services/electron/SecurePasswords.js new file mode 100644 index 00000000..b96e16ea --- /dev/null +++ b/src/services/electron/SecurePasswords.js @@ -0,0 +1,29 @@ +import PopupService from "../utility/PopupService"; +import {Popup} from "../../models/popups/Popup"; +const {Wallet} = window.require('electron').remote.getGlobal('appShared'); + +export default class SecurePasswords { + + static async verifyPassword(){ + return new Promise(async resolve => { + PopupService.push(Popup.verifyPassword(async password => { + if(!password) return resolve(false); + resolve(await Wallet.verifyPassword(password)) + }, true)); + }) + } + + static async changePassword(){ + return new Promise(async resolve => { + PopupService.push(Popup.verifyPassword(async password => { + if(!password) return resolve(false); + resolve(await Wallet.changePassword(password)) + }, true)); + }) + } + + static exportPrivateKey(keypair){ + PopupService.push(Popup.exportPrivateKey(keypair)); + return true; + } +} \ No newline at end of file diff --git a/src/services/electron/StorageService.js b/src/services/electron/StorageService.js index ac3322b0..42d7b0a5 100644 --- a/src/services/electron/StorageService.js +++ b/src/services/electron/StorageService.js @@ -1,6 +1,7 @@ import {RUNNING_TESTS} from "@walletpack/core/util/TestingHelper"; import * as Actions from '@walletpack/core/store/constants'; const Store = window.require('electron-store'); +const {Wallet} = window.require('electron').remote.getGlobal('appShared'); const ABIS_NAME = 'abi'; const HISTORIES_NAME = 'histories'; @@ -107,6 +108,7 @@ export default class StorageService { abiStorage().clear(); historyStorage().clear(); translationStorage().clear(); + Wallet.setScatter(null); // TODO: Clear main process return true; } diff --git a/src/services/utility/BackupService.js b/src/services/utility/BackupService.js index bb7b52c2..3fe1222f 100644 --- a/src/services/utility/BackupService.js +++ b/src/services/utility/BackupService.js @@ -1,36 +1,9 @@ import * as Actions from '@walletpack/core/store/constants'; -import {BACKUP_STRATEGIES} from '@walletpack/core/models/Settings'; import StorageService from '../../services/electron/StorageService'; import {store} from "../../store/store"; -const saveBackup = (filepath) => { - const scatter = StorageService.getScatter(); - const date = new Date(); - const month = date.getUTCMonth(); - const year = date.getUTCFullYear(); - const salt = StorageService.getSalt(); - const file = scatter + '|SLT|' + salt; - const name = `scatter__${store.state.scatter.hash.substr(0,4)}-${store.state.scatter.hash.slice(-4)}__${store.state.scatter.meta.version}__${month}-${year}.json`; - - return StorageService.saveFile(filepath, name, file); -}; - export default class BackupService { - static async setBackupStrategy(strategy){ - const scatter = store.state.scatter.clone(); - scatter.settings.autoBackup = strategy; - return store.dispatch(Actions.SET_SCATTER, scatter); - } - - static async createBackup(){ - - const location = StorageService.getFolderLocation(); - if(! location) return false; - - return await saveBackup(location[0]); - } - static async setBackupLocation(location = null){ if(!location) location = (() => { const f = StorageService.getFolderLocation(); @@ -51,16 +24,4 @@ export default class BackupService { return this.setBackupLocation(backupPath); } - static async createAutoBackup(){ - if(!store.state.scatter || !store.state.scatter.settings) return; - const strategy = store.state.scatter.settings.autoBackup; - if(!strategy || !strategy.length || strategy === BACKUP_STRATEGIES.MANUAL) return; - - const backupLocation = store.state.scatter.settings.backupLocation; - if(!backupLocation || !backupLocation.length) return false; - - - return await saveBackup(backupLocation); - } - } \ No newline at end of file diff --git a/src/services/utility/WebHashChecker.js b/src/services/utility/WebHashChecker.js new file mode 100644 index 00000000..a900ad22 --- /dev/null +++ b/src/services/utility/WebHashChecker.js @@ -0,0 +1,46 @@ +import {GET} from "@walletpack/core/services/apis/BackendApiService"; + +const sha256 = require('eosjs-ecc').sha256; + + +const HOST = `http://localhost:8081/`; + +const getIndexHTML = () => { + return fetch(HOST).then(x => x.text()).catch(() => null); +}; + +const getJavaScriptSource = filename => { + return fetch(`${HOST}/${filename}`).then(x => x.text()).catch(() => null); +}; + +export default class WebHashChecker { + + static async check(){ + const html = await getIndexHTML(); + console.log('html', html); + if(!html) return {error:'Could not get Scatter Embed "html" hash.'}; + + const mainBundle = await getJavaScriptSource('main.bundle.js'); + if(!mainBundle) return {error:'Could not get Scatter Embed "mainBundle" hash.'}; + + const bundleSources = {}; + for(let i = 0; i < 30; i++){ + const src = await getJavaScriptSource(`${i}.bundle.js`); + if(!src) console.error('no source for ', i); + else { + bundleSources[i] = sha256(src); + } + } + + const bundleHash = sha256([sha256(html), sha256(mainBundle)].concat(Object.keys(bundleSources).sort().map(key => { + return bundleSources[key]; + })).join('')); + + const apiHash = await GET(`embed/hash`).then(x => x.json()).catch(() => null); + if(!apiHash) return {error:'Could not get Scatter Embed hash from API.'}; + + console.log(bundleSources, bundleHash); + return apiHash === bundleHash; + } + +} \ No newline at end of file diff --git a/src/store/actions.js b/src/store/actions.js index 2eb19ad0..cafe3511 100644 --- a/src/store/actions.js +++ b/src/store/actions.js @@ -1,5 +1,4 @@ import * as Actions from '@walletpack/core/store/constants' -import StorageService from '../services/electron/StorageService'; import BackupService from '../services/utility/BackupService'; import Hasher from '@walletpack/core/util/Hasher' import IdGenerator from '@walletpack/core/util/IdGenerator' diff --git a/src/styles/styles.scss b/src/styles/styles.scss index f44a9314..f8674e70 100644 --- a/src/styles/styles.scss +++ b/src/styles/styles.scss @@ -1,5 +1,7 @@ @import "./variables"; + + ::-webkit-scrollbar { width: 8px; } diff --git a/src/util/ElectronHelpers.js b/src/util/ElectronHelpers.js index b4659908..71257d9c 100644 --- a/src/util/ElectronHelpers.js +++ b/src/util/ElectronHelpers.js @@ -4,7 +4,7 @@ let electron = window.require('electron'); export const remote = electron.remote; export const ipcRenderer = electron.ipcRenderer; const {clipboard, shell} = electron; -const {reloader, Transport} = remote.getGlobal('appShared'); +const {reloader, Transport, Wallet, NotificationService} = remote.getGlobal('appShared'); import ScatterCore from "@walletpack/core"; import StorageService from "../services/electron/StorageService"; @@ -12,7 +12,6 @@ import {store} from "../store/store"; import WindowService from "../services/electron/WindowService"; import SocketService from "../services/electron/SocketService"; import ExternalWallet from "@walletpack/core/models/hardware/ExternalWallet"; -import LedgerWallet from "../models/hardware/LedgerWallet"; let popupService; const PopupService = () => { @@ -71,19 +70,6 @@ export default class ElectronHelpers { static initializeCore(){ ElectronHelpers.bindContextMenu(); - - - - const eventListener = async (type, data) => { - // console.log('event listener', type, data); - if(type === 'popout') { - const popup = new Popup(PopupDisplayTypes.POP_OUT, new PopupData(data.type, data)); - return await WindowService.openPopOut(popup); - } - - }; - - ScatterCore.initialize( { blockchains:{ @@ -100,54 +86,30 @@ export default class ElectronHelpers { ] }, store, - { + /* security */ { getSalt:StorageService.getSalt, - get:() => ipcAsync('seed'), - set:(seed) => ipcFaF('seeding', seed), - clear:() => ipcFaF('key', null), }, - { - getVersion:ElectronHelpers.getVersion, - pushNotification:ElectronHelpers.pushNotificationMethod(), + /* framwork */ { + getVersion:remote.app.getVersion, + pushNotification:NotificationService.pushNotification, }, - eventListener, - { - socketService:SocketService, - publicToPrivate:async publicKey => { - return false; - } - } + /* eventListener */ () => {}, + { socketService:SocketService } ); - // TODO: Ledger - // ExternalWallet.loadWallets([ - // {name:'LEDGER', wallet:LedgerWallet} - // ]) - - } - - static getLedgerTransport(){ - return Transport.default - } - - static getVersion(){ - return remote.app.getVersion(); - } - - static pushNotificationMethod(){ - return remote ? remote.getGlobal('appShared').NotificationService.pushNotification : () => {}; } static reload(){ + Wallet.reloading(); reloader(); } static copy(txt){ clipboard.writeText(txt); - PopupService().push(Popup.snackbar("Copied to clipboard", 'check')) } static openLinkInBrowser(link, filepath = false){ + console.log('link', link, filepath); if(filepath) shell.openItem(link); else { if(link.indexOf('https://') === 0 || link.indexOf('http://') === 0) { diff --git a/src/util/WalletTalk.js b/src/util/WalletTalk.js index 7dcc7c78..0928d2a2 100644 --- a/src/util/WalletTalk.js +++ b/src/util/WalletTalk.js @@ -11,6 +11,7 @@ const services = { StorageService:require('../services/electron/StorageService').default, WindowService:require('../services/electron/WindowService').default, Injectable:require('../services/electron/Injectable').default, + SecurePasswords:require('../services/electron/SecurePasswords').default, } export default class WalletTalk { @@ -29,6 +30,8 @@ export default class WalletTalk { 'StorageService', 'WindowService', 'Injectable', + 'BackupService', + 'SecurePasswords', ].includes(service)) return; // console.log('Propagated from embed: ', service, method, data, id); if(service === 'StorageService'){ diff --git a/src/views/Login.vue b/src/views/Login.vue index 4cdb7838..8e3b82d8 100644 --- a/src/views/Login.vue +++ b/src/views/Login.vue @@ -1,8 +1,6 @@