diff --git a/README.md b/README.md index a1cd4d90..ca76fa45 100644 --- a/README.md +++ b/README.md @@ -47,28 +47,20 @@ If you wish to compile the app, install the following also: ``` cd wallet ``` - -3. Clone iri: - - ``` - git clone https://github.com/iotaledger/iri - ``` - - Note: make sure compiled iri.jar is in the `iri` folder. -4. Install components +3. Install components ``` npm install ``` -5. Run the app: +4. Run the app: ``` npm start ``` -6. If you wish to compile the app: +5. If you wish to compile the app: ``` npm run compile @@ -86,4 +78,4 @@ If you wish to compile the app, install the following also: #### Testnet -To build testnet binaries, rename `package.testnet.json` to `package.json` and follow instructions as above. Make sure the jar is named `iri-testnet.jar`. \ No newline at end of file +To build testnet binaries, rename `package.testnet.json` to `package.json` and follow instructions as above. diff --git a/app/js/main.js b/app/js/main.js index 65a968d0..40bfbf03 100644 --- a/app/js/main.js +++ b/app/js/main.js @@ -29,6 +29,9 @@ var __entityMap = { "/": '/' }; +var IRI_VERSION = '1.4.2.1' +var IRI_DIRECTORY = 'iri' + String.prototype.escapeHTML = function() { return String(this).replace(/[&<>"'\/]/g, function(s) { return __entityMap[s]; @@ -43,9 +46,9 @@ var App = (function(App, undefined) { var isStarted = false; var appDirectory = ""; var appDataDirectory = ""; + var tempDirectory = ""; var resourcesDirectory = ""; var databaseDirectory = ""; - var jarDirectory = ""; var javaLocations = []; var selectedJavaLocation; var currentLocationTest = 0; @@ -908,12 +911,12 @@ var App = (function(App, undefined) { appDataDirectory = path.join(electron.app.getPath("appData"), "IOTA Wallet" + (isTestNet ? " Testnet" : "")); } + tempDirectory = electron.app.getPath('temp') + App.loadSettings(); databaseDirectory = (settings.dbLocation ? settings.dbLocation : path.join(appDataDirectory, "iri")); - jarDirectory = path.join(resourcesDirectory, "iri"); - if (!fs.existsSync(appDataDirectory)) { fs.mkdirSync(appDataDirectory); } @@ -1010,11 +1013,11 @@ var App = (function(App, undefined) { App.start = function() { if (settings.lightWallet == 1 && (!settings.lightWalletHost || !settings.lightWalletPort)) { - App.showSetupWindow({"section": "light-node"}); + App.showSetupWindow({"section": "light-node", appDataDirectory, tempDirectory}); } else if (settings.lightWallet == 0 && settings.nodes.length == 0) { - App.showSetupWindow({"section": "full-node"}); + App.showSetupWindow({"section": "full-node", appDataDirectory, tempDirectory}); } else if (settings.lightWallet == -1) { - App.showSetupWindow(); + App.showSetupWindow({appDataDirectory, tempDirectory}); } else if (settings.lightWallet == 1) { global.lightWallet = true; App.startLightNode(); @@ -1228,7 +1231,7 @@ var App = (function(App, undefined) { params.push("-jar"); - params.push(path.join(jarDirectory, "iri" + (isTestNet ? "-testnet" : "") + ".jar")); + params.push(path.join(appDataDirectory, IRI_DIRECTORY, 'iri' + (isTestNet ? '-testnet' : '') + '-' + IRI_VERSION + '.jar')); // temporary ! // Only rescan once @@ -1802,7 +1805,10 @@ var App = (function(App, undefined) { "lightWalletPort" : settings.lightWalletPort, "port" : settings.port, "nodes" : settings.nodes, - "section" : params && params.section ? params.section : null}); + "section" : params && params.section ? params.section : null, + "appDataDirectory": params && params.appDataDirectory ? params.appDataDirectory : null, + "tempDirectory" : params && params.tempDirectory ? params.tempDirectory : null + }); } App.showInitializationAlertWindow = function(title, msg) { @@ -1851,14 +1857,19 @@ var App = (function(App, undefined) { "java64BitsOK" : java64BitsOK, "is64BitOS" : is64BitOS, "port" : settings.port, - "nodes" : settings.nodes}); + "nodes" : settings.nodes, + "appDataDirectory" : appDataDirectory, + "tempDirectory" : tempDirectory}); }); } else { App.showWindow("init_error.html", {"title" : title, "message" : msg, "serverOutput" : serverOutput, "port" : settings.port, - "nodes" : settings.nodes}); + "nodes" : settings.nodes, + "appDataDirectory" : appDataDirectory, + "tempDirectory" : tempDirectory + }); } selectedJavaLocation = ""; @@ -1971,7 +1982,9 @@ var App = (function(App, undefined) { //ready-to-show event not working.. otherWin.webContents.once("did-finish-load", function() { App.updateTitle(); - //win.webContents.toggleDevTools({"mode": "undocked"}); + if (isDebug) { + otherWin.webContents.openDevTools({"mode": "undocked"}); + } otherWin.webContents.send("show", params); }); diff --git a/app/windows/css/style.css b/app/windows/css/style.css index a232055c..cfc84bef 100644 --- a/app/windows/css/style.css +++ b/app/windows/css/style.css @@ -28,7 +28,7 @@ body { #footer { position: absolute; bottom: 0; - left: 0; + left: 0; right: 0; padding: 20px; background-color: #f1f1f1; @@ -162,4 +162,38 @@ p.note { .error { display: none; color: red; -} \ No newline at end of file +} + + +#download-iri-success { + display: none; +} + +#download-iri-progress { + display: none; + padding-bottom: 20px; +} + +#download-iri-progress-bar { + width: 100%; + height: 10px; + margin: 10px 0px; + background: #f0f0f0; + box-shadow: inset 3px 0px 10px rgba(0,0,0,0.14); + border-radius: 5px; + overflow: hidden; + position: relative; +} + +#download-iri-progress-bar-content { + transform: scaleX(0); + transform-origin: left; + width: 100%; + height: 10px; + border-radius: 10px; + box-shadow: 4px 1px 20px rgba(0,0,0,0.3); + background: linear-gradient(90deg, #4CAF50, #00E676); + position: absolute; + top: 0px; + left: 0px; +} diff --git a/app/windows/js/init_error.js b/app/windows/js/init_error.js index 6e286644..61ad8b7f 100644 --- a/app/windows/js/init_error.js +++ b/app/windows/js/init_error.js @@ -21,6 +21,8 @@ String.prototype.escapeHTML = function() { var UI = (function(UI, undefined) { var isLightWallet = false; + var _tempDirectory + var _appDataDirectory UI.initialize = function() { document.getElementById("quit-btn").addEventListener("click", function(e) { @@ -82,6 +84,8 @@ var UI = (function(UI, undefined) { UI.show = function(params) { if (params) { + _appDataDirectory = params.appDataDirectory + _tempDirectory = params.tempDirectory isLightWallet = params.lightWallet == 1; if (params.title) { document.getElementById("title").innerHTML = UI.format(params.title); @@ -130,7 +134,7 @@ var UI = (function(UI, undefined) { } UI.showSetupWindow = function(section) { - electron.ipcRenderer.send("showSetupWindow", {"section": section}); + electron.ipcRenderer.send("showSetupWindow", {"section": section, appDataDirectory: _appDataDirectory, tempDirectory: _tempDirectory}); } UI.updateContentSize = function() { @@ -203,4 +207,4 @@ electron.ipcRenderer.on("changeLanguage", function(event, language) { UI.changeLanguage(language, function() { UI.updateContentSize(); }); -}); \ No newline at end of file +}); diff --git a/app/windows/js/setup.js b/app/windows/js/setup.js index 5084d335..543dad12 100644 --- a/app/windows/js/setup.js +++ b/app/windows/js/setup.js @@ -1,6 +1,89 @@ -const electron = require("electron"); -const path = require("path"); -const https = require("https"); +const electron = require('electron') +const path = require('path') +const https = require('https') +const request = require('request') +const fs = require('fs') +const openpgp = require('openpgp') + +const IRI_VERSION = '1.4.2.1' +const IRI_DIRECTORY = 'iri' +const IRI_RELEASE_URI = `https://github.com/iotaledger/iri/releases/download/v${IRI_VERSION}/iri-${IRI_VERSION}.jar` +const IRI_RELEASE_PUB_KEY = `-----BEGIN PGP PUBLIC KEY BLOCK----- + +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 +=fX8W +-----END PGP PUBLIC KEY BLOCK-----` var isDevelopment = String(process.env.NODE_ENV).trim() === "development"; var resourcesDirectory = isDevelopment ? "../../" : "../../../"; @@ -23,6 +106,8 @@ String.prototype.escapeHTML = function() { var UI = (function(UI, undefined) { var _updateNodeConfiguration = false; var _lightWalletHosts = []; + var _appDataDirectory + var _tempDirectory UI.fetchProviders = function (urls) { return Promise.all(urls.map(url => { @@ -55,7 +140,6 @@ var UI = (function(UI, undefined) { hosts.push(host) } })) - console.log('PROVIDERS', hosts) if (!hosts.length) { return hosts } @@ -87,8 +171,14 @@ var UI = (function(UI, undefined) { }); document.getElementById("light-node-btn").addEventListener("click", UI.showLightNodeSection); - document.getElementById("full-node-btn").addEventListener("click", UI.showFullNodeSection); - document.getElementById("switch-btn").addEventListener("click", UI.showOtherNodeSection); + document.getElementById("full-node-btn").addEventListener("click", function() { + UI.showFullNodeSection({appDataDirectory: _appDataDirectory, tempDirectory: _tempDirectory}) + }); + document.getElementById("switch-btn").addEventListener("click", function () { + UI.showOtherNodeSection({appDataDirectory: _appDataDirectory, tempDirectory: _tempDirectory}) + }); + + document.getElementById('download-iri-btn').addEventListener('click', UI.downloadIRI) document.getElementById("quit-btn").addEventListener("click", function(e) { document.getElementById("quit-btn").disabled = true; @@ -190,13 +280,26 @@ var UI = (function(UI, undefined) { UI.updateContentSize(); } - UI.showFullNodeSection = function() { + UI.showFullNodeSection = function(params) { + _appDataDirectory = params.appDataDirectory + _tempDirectory = params.tempDirectory + let title = '' + if (fileExists(path.join(_appDataDirectory, IRI_DIRECTORY, `iri-${IRI_VERSION}.jar`))) { + renderFullNodeConfigurationSection() + title = 'full_node_settings' + document.getElementById("start-btn").style.display = "block" + } else { + renderDownloadIRISection() + title = 'download_iri_prompt' + document.getElementById('download-iri-btn').innerHTML = UI.t('download_iri') + '-' + IRI_VERSION + document.getElementById("start-btn").style.display = "none" + } + document.getElementById("node-choice").style.display = "none"; - UI.changeElementLanguage("title", "full_node_settings"); + UI.changeElementLanguage("title", title); document.getElementById("message").style.display = "none"; document.getElementById("light-node-section").style.display = "none"; document.getElementById("full-node-section").style.display = "block"; - document.getElementById("start-btn").style.display = "block"; document.getElementById("switch-btn").style.display = "block"; UI.changeElementLanguage("switch-btn", "switch_to_light_node"); document.getElementById("quit-btn").style.display = "none"; @@ -217,9 +320,9 @@ var UI = (function(UI, undefined) { UI.updateContentSize(); } - UI.showOtherNodeSection = function() { + UI.showOtherNodeSection = function(params) { if (document.getElementById("light-node-section").style.display == "block") { - UI.showFullNodeSection(); + UI.showFullNodeSection(params); UI.changeElementLanguage("switch-btn", "switch_to_light_node"); } else { UI.showLightNodeSection(); @@ -257,6 +360,8 @@ var UI = (function(UI, undefined) { document.getElementById("switch-btn").style.display = "none"; if (params) { + _appDataDirectory = params.appDataDirectory + _tempDirectory = params.tempDirectory if (params.lightWalletHost) { document.getElementById("host").value = params.lightWalletHost + (params.lightWalletPort ? ":" + params.lightWalletPort : ""); } @@ -270,7 +375,7 @@ var UI = (function(UI, undefined) { if (params.section == "light-node") { UI.showLightNodeSection(); } else if (params.section == "full-node") { - UI.showFullNodeSection(); + UI.showFullNodeSection(params); } } } @@ -292,6 +397,195 @@ var UI = (function(UI, undefined) { electron.remote.getCurrentWindow().setContentSize(600, parseInt(document.documentElement.scrollHeight, 10) + parseInt(document.getElementById("footer").scrollHeight, 10), false); } + UI.downloadIRI = function () { + const fileName = `iri-${IRI_VERSION}.jar` + const tempFileName = `iri-${IRI_VERSION}-unverified.jar` + const iriDirectory = path.join(_appDataDirectory, IRI_DIRECTORY) + const filePath = path.join(iriDirectory, fileName) + const tempFilePath = path.join(_tempDirectory, tempFileName) + + hideDownloadErrors() + disableButtons() + + downloadFile(IRI_RELEASE_URI, tempFilePath, { + onResponse: (size) => { + renderDownloadStatus() + renderDownloadProgress(0, size) + }, + onData: renderDownloadProgress + }) + + .then(() => { + downloadFile(`${IRI_RELEASE_URI}.asc`, `${tempFilePath}.asc`, { + onResponse: () => {}, + onData: () => {} + }) + .then(() => { + renderVerificationStatus() + return verifyFileSignature( + fs.readFileSync(tempFilePath), + fs.readFileSync(`${tempFilePath}.asc`, { encoding: 'utf8' }), + IRI_RELEASE_PUB_KEY + ) + }) + .then(valid => { + hideVerificationStatus() + if (!valid) { + return renderDownloadErrors(`Signature verification failed for downloaded file: ${tempFileName}`) + } + renameFile(tempFilePath, filePath) + .then(() => renameFile(`${tempFilePath}.asc`, `${filePath}.asc`)) + .then(() => renderDownloadSuccess()) + .catch(err => renderDownloadErrors(`Failed to save ${fileName}.`, err)) + }) + }) + + .catch(err => { + hideVerificationStatus() + renderDownloadErrors(`Failed to download ${fileName}`, err) + }) + } + + function downloadFile (uri, destination, hooks) { + return new Promise((resolve, reject) => { + let size = 0 + let bytesReceived = 0 + + let file = fs.createWriteStream(destination) + + request + .get(uri) + .on('response', data => { + size = parseInt(data.headers['content-length']) + hooks.onResponse(size) + }) + .on('data', chunk => { + bytesReceived += chunk.length + hooks.onData(bytesReceived, size) + }) + .on('error', err => reject(err)) + .on('end', () => resolve(file)) + .pipe(file) + }) + } + + function verifyFileSignature (file, signature, publicKey) { + const options = { + message: openpgp.message.fromBinary(file), + signature: openpgp.signature.readArmored(signature), + publicKeys: openpgp.key.readArmored(publicKey).keys + } + + return openpgp.verify(options) + .then(verified => verified.signatures[0].valid) + } + + function fileExists (path) { + return fs.existsSync(path) + } + + function renameFile (oldPath, newPath) { + return new Promise((resolve, reject) => + fs.rename(oldPath, newPath, err => { + if (err) reject(err) + else resolve() + }) + ) + } + + function renderDownloadIRISection () { + document.getElementById('full-node-download-iri-section').style.display = 'block' + document.getElementById('full-node-configuration-section').style.display = 'none' + document.getElementById('download-iri-verification-status').style.display = 'none' + document.getElementById('download-iri-verification-status').style.display = 'none' + UI.updateContentSize() + } + + function renderFullNodeConfigurationSection () { + document.getElementById('full-node-download-iri-section').style.display = 'none' + document.getElementById('full-node-configuration-section').style.display = 'block' + UI.updateContentSize() + } + + function disableButtons () { + document.getElementById('download-iri-prompt').style.display = 'none' + document.getElementById('download-iri-btn').disabled = true + document.getElementById('switch-btn').disabled = true + UI.updateContentSize() + } + + function enableButtons () { + document.getElementById('download-iri-prompt').style.display = 'block' + document.getElementById('download-iri-btn').style.display = 'block' + document.getElementById('download-iri-btn').disabled = false + document.getElementById('switch-btn').disabled = false + UI.updateContentSize() + } + + function renderDownloadStatus () { + document.getElementById('download-iri-progress').style.display = 'block' + document.getElementById('download-iri-btn').style.display = 'none' + document.getElementById('download-iri-verification-status').style.display = 'none' + document.getElementById('download-iri-success').style.display = 'none' + document.getElementById('download-iri-error').style.display = 'none' + UI.updateContentSize() + } + + function renderDownloadErrors (...errors) { + hideVerificationStatus() + const el = document.getElementById('download-iri-error') + el.style.display = 'block' + for (const err of Object.keys(errors)) { + const errEl = document.createElement('div') + errEl.innerHTML = UI.format(errors[err]) + el.append(errEl) + } + enableButtons() + } + + function hideDownloadErrors () { + const el = document.getElementById('download-iri-error') + el.style.display = 'none' + while (el.hasChildNodes()) { + el.removeChild(el.lastChild) + } + UI.updateContentSize() + } + + function renderDownloadProgress (received, size) { + document.getElementById('download-iri-progress').style.display = 'block' + const dx = (received * 100) / size + document.getElementById('download-iri-progress-percentage').innerHTML = UI.format(`${parseFloat(dx).toFixed(2)} %`) + document.getElementById('download-iri-progress-bar-content').style.transform = `scaleX(${(dx / 100).toString()})` + } + + function renderVerificationStatus () { + document.getElementById('download-iri-progress').style.display = 'none' + document.getElementById('download-iri-verification-status').style.display = 'block' + UI.updateContentSize() + } + + function hideVerificationStatus () { + document.getElementById('download-iri-verification-status').style.display = 'none' + UI.updateContentSize() + } + + function renderDownloadSuccess () { + document.getElementById('download-iri-success').style.display = 'block' + document.getElementById('download-iri-btn').style.display = 'none' + document.getElementById('download-iri-verification-status').style.display = 'none' + document.getElementById('switch-btn').disabled = false + UI.updateContentSize() + return new Promise((resolve, reject) => + setTimeout(() => { + UI.changeElementLanguage("title", 'full_node_settings'); + document.getElementById('start-btn').style.display = 'block' + renderFullNodeConfigurationSection() + resolve() + }, 2000) + ) + } + UI.makeMultilingual = function(currentLanguage, callback) { i18n = i18next .use(window.i18nextXHRBackend) diff --git a/app/windows/setup.html b/app/windows/setup.html index 560ea092..eb1f39b3 100644 --- a/app/windows/setup.html +++ b/app/windows/setup.html @@ -16,7 +16,7 @@

-

+

@@ -26,11 +26,31 @@

-

:

- -

:

- -

: udp://ip:12345

+
+

:

+ +

:

+ +

: udp://ip:12345

+
+
+
+

+
+

+
+
+
+
+
+
+ +
+
+ +
+ +
@@ -45,4 +65,4 @@

require("./js/setup.js") - \ No newline at end of file + diff --git a/locales/en/translation.json b/locales/en/translation.json index ce137d33..56c78060 100644 --- a/locales/en/translation.json +++ b/locales/en/translation.json @@ -361,5 +361,11 @@ "recovery_old_seed_hash_balance": "Old seed has balance. Please move the balance to a new seed and try again.", "recovery_submitted_address": "Any reclaimed tokens will be sent to your address shown below. For security reasons, do not spend from this address until you receive all your funds.", "recovery_unexpected_error": "An unxpected error occured. Please try again.", - "recovery_receive_address": "Address:" + "recovery_receive_address": "Address:", + "download_iri_prompt": "Download IRI", + "download_iri_prompt_text": "Click the button below to start downloading IRI. Wallet fetches IRI from official releases page and automatically verifies the file signature.", + "download_iri": "Download iri", + "download_iri_downloading": "Downloading IRI...", + "download_iri_verifying": "Verifying signature...", + "download_iri_success": "Downloaded IRI successfully" } diff --git a/package-lock.json b/package-lock.json index 28c4c69c..5fe76a69 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,7 +10,9 @@ "integrity": "sha512-+rr4OgeTNrLuJAf09o3USdttEYiXvZshWMkhD6wR9v1ieXH0JM1Q2yT41/cJuJcqiPpSXlM/g3aR+Y5MWQdr0Q==", "dev": true, "requires": { - "7zip-bin-linux": "1.1.0" + "7zip-bin-linux": "1.1.0", + "7zip-bin-mac": "1.0.1", + "7zip-bin-win": "2.1.1" } }, "7zip-bin-linux": { @@ -20,6 +22,20 @@ "dev": true, "optional": true }, + "7zip-bin-mac": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/7zip-bin-mac/-/7zip-bin-mac-1.0.1.tgz", + "integrity": "sha1-Pmh3i78JJq3GgVlCcHRQXUdVXAI=", + "dev": true, + "optional": true + }, + "7zip-bin-win": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/7zip-bin-win/-/7zip-bin-win-2.1.1.tgz", + "integrity": "sha512-6VGEW7PXGroTsoI2QW3b0ea95HJmbVBHvfANKLLMzSzFA1zKqVX5ybNuhmeGpf6vA0x8FJTt6twpprDANsY5WQ==", + "dev": true, + "optional": true + }, "@types/node": { "version": "7.0.46", "resolved": "https://registry.npmjs.org/@types/node/-/node-7.0.46.tgz", @@ -30,7 +46,6 @@ "version": "5.2.3", "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.2.3.tgz", "integrity": "sha1-wG9Zh3jETGsWGrr+NGa4GtGBTtI=", - "dev": true, "requires": { "co": "4.6.0", "fast-deep-equal": "1.0.0", @@ -192,14 +207,12 @@ "asn1": { "version": "0.2.3", "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.3.tgz", - "integrity": "sha1-2sh4dxPJlmhJ/IGAd36+nB3fO4Y=", - "dev": true + "integrity": "sha1-2sh4dxPJlmhJ/IGAd36+nB3fO4Y=" }, "assert-plus": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", - "dev": true + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" }, "async-exit-hook": { "version": "2.0.1", @@ -210,20 +223,17 @@ "asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", - "dev": true + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" }, "aws-sign2": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", - "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=", - "dev": true + "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=" }, "aws4": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.6.0.tgz", - "integrity": "sha1-g+9cqGCysy5KDe7e6MdxudtXRx4=", - "dev": true + "integrity": "sha1-g+9cqGCysy5KDe7e6MdxudtXRx4=" }, "balanced-match": { "version": "1.0.0", @@ -240,7 +250,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.1.tgz", "integrity": "sha1-Y7xdy2EzG5K8Bf1SiVPDNGKgb40=", - "dev": true, "optional": true, "requires": { "tweetnacl": "0.14.5" @@ -311,7 +320,6 @@ "version": "4.3.1", "resolved": "https://registry.npmjs.org/boom/-/boom-4.3.1.tgz", "integrity": "sha1-T4owBctKfjiJ90kDD9JbluAdLjE=", - "dev": true, "requires": { "hoek": "4.2.0" } @@ -468,8 +476,7 @@ "caseless": { "version": "0.12.0", "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", - "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=", - "dev": true + "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=" }, "chalk": { "version": "2.1.0", @@ -520,8 +527,7 @@ "co": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", - "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=", - "dev": true + "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=" }, "code-point-at": { "version": "1.1.0", @@ -548,7 +554,6 @@ "version": "1.0.5", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.5.tgz", "integrity": "sha1-k4NwpXtKUd6ix3wV1cX9+JUWQAk=", - "dev": true, "requires": { "delayed-stream": "1.0.0" } @@ -656,7 +661,6 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-3.1.2.tgz", "integrity": "sha1-qJ+7Ig9c4l7FboxKqKT9e1sNKf4=", - "dev": true, "requires": { "boom": "5.2.0" }, @@ -665,7 +669,6 @@ "version": "5.2.0", "resolved": "https://registry.npmjs.org/boom/-/boom-5.2.0.tgz", "integrity": "sha512-Z5BTk6ZRe4tXXQlkqftmsAUANpXmuwlsF5Oov8ThoMbQRzdGTA1ngYRW160GexgOgjsFOKJz0LYhoNi+2AMBUw==", - "dev": true, "requires": { "hoek": "4.2.0" } @@ -718,7 +721,6 @@ "version": "1.14.1", "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", - "dev": true, "requires": { "assert-plus": "1.0.0" } @@ -746,8 +748,7 @@ "delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", - "dev": true + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" }, "delegates": { "version": "1.0.0", @@ -812,7 +813,6 @@ "version": "0.1.1", "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz", "integrity": "sha1-D8c6ntXw1Tw4GTOYUj735UN3dQU=", - "dev": true, "optional": true, "requires": { "jsbn": "0.1.1" @@ -1008,6 +1008,14 @@ "mime": "2.0.3" } }, + "encoding": { + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.12.tgz", + "integrity": "sha1-U4tm8+5izRq1HsMjgp0flIDHS+s=", + "requires": { + "iconv-lite": "0.4.19" + } + }, "end-of-stream": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.0.tgz", @@ -1074,8 +1082,7 @@ "extend": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.1.tgz", - "integrity": "sha1-p1Xqe8Gt/MWjHOfnYtuq3F5jZEQ=", - "dev": true + "integrity": "sha1-p1Xqe8Gt/MWjHOfnYtuq3F5jZEQ=" }, "extract-zip": { "version": "1.6.5", @@ -1109,14 +1116,12 @@ "extsprintf": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", - "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=", - "dev": true + "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=" }, "fast-deep-equal": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.0.0.tgz", - "integrity": "sha1-liVqO8l1WV6zbYLpkp0GDYk0Of8=", - "dev": true + "integrity": "sha1-liVqO8l1WV6zbYLpkp0GDYk0Of8=" }, "fd-slicer": { "version": "1.0.1", @@ -1161,14 +1166,12 @@ "forever-agent": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", - "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=", - "dev": true + "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=" }, "form-data": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.1.tgz", "integrity": "sha1-b7lPvXGIUwbXPRXMSX/kzE7NRL8=", - "dev": true, "requires": { "asynckit": "0.4.0", "combined-stream": "1.0.5", @@ -1260,7 +1263,6 @@ "version": "0.1.7", "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", - "dev": true, "requires": { "assert-plus": "1.0.0" } @@ -1328,14 +1330,12 @@ "har-schema": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", - "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=", - "dev": true + "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=" }, "har-validator": { "version": "5.0.3", "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.0.3.tgz", "integrity": "sha1-ukAsJmGU8VlW7xXg/PJCmT9qff0=", - "dev": true, "requires": { "ajv": "5.2.3", "har-schema": "2.0.0" @@ -1357,19 +1357,17 @@ "version": "6.0.2", "resolved": "https://registry.npmjs.org/hawk/-/hawk-6.0.2.tgz", "integrity": "sha512-miowhl2+U7Qle4vdLqDdPt9m09K6yZhkLDTWGoUiUzrQCn+mHHSmfJgAyGaLRZbPmTqfFFjRV1QWCW0VWUJBbQ==", - "dev": true, "requires": { "boom": "4.3.1", "cryptiles": "3.1.2", "hoek": "4.2.0", - "sntp": "2.0.2" + "sntp": "2.1.0" } }, "hoek": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/hoek/-/hoek-4.2.0.tgz", - "integrity": "sha512-v0XCLxICi9nPfYrS9RL8HbYnXi9obYAeLbSP00BmnZwCK9+Ih9WOjoZ8YoHCoav2csqn4FOz4Orldsy2dmDwmQ==", - "dev": true + "integrity": "sha512-v0XCLxICi9nPfYrS9RL8HbYnXi9obYAeLbSP00BmnZwCK9+Ih9WOjoZ8YoHCoav2csqn4FOz4Orldsy2dmDwmQ==" }, "home-path": { "version": "1.0.5", @@ -1387,7 +1385,6 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", - "dev": true, "requires": { "assert-plus": "1.0.0", "jsprim": "1.4.1", @@ -1410,8 +1407,7 @@ "iconv-lite": { "version": "0.4.19", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.19.tgz", - "integrity": "sha512-oTZqweIP51xaGPI4uPa56/Pri/480R+mo7SeU+YETByQNhDG55ycFyNLIgta9vXhILrxXDmF7ZGhqZIcuN0gJQ==", - "dev": true + "integrity": "sha512-oTZqweIP51xaGPI4uPa56/Pri/480R+mo7SeU+YETByQNhDG55ycFyNLIgta9vXhILrxXDmF7ZGhqZIcuN0gJQ==" }, "import-lazy": { "version": "2.1.0", @@ -1422,8 +1418,7 @@ "imurmurhash": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", - "dev": true + "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=" }, "indent-string": { "version": "2.1.0", @@ -1554,14 +1549,12 @@ "is-stream": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", - "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", - "dev": true + "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=" }, "is-typedarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", - "dev": true + "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" }, "is-utf8": { "version": "0.2.1", @@ -1590,8 +1583,7 @@ "isstream": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", - "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=", - "dev": true + "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=" }, "js-yaml": { "version": "3.10.0", @@ -1607,26 +1599,22 @@ "version": "0.1.1", "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", - "dev": true, "optional": true }, "json-schema": { "version": "0.2.3", "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", - "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=", - "dev": true + "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=" }, "json-schema-traverse": { "version": "0.3.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz", - "integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A=", - "dev": true + "integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A=" }, "json-stable-stringify": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz", "integrity": "sha1-mnWdOcXy/1A/1TAGRu1EX4jE+a8=", - "dev": true, "requires": { "jsonify": "0.0.0" } @@ -1634,8 +1622,7 @@ "json-stringify-safe": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", - "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=", - "dev": true + "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=" }, "json5": { "version": "0.4.0", @@ -1653,14 +1640,12 @@ "jsonify": { "version": "0.0.0", "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz", - "integrity": "sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM=", - "dev": true + "integrity": "sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM=" }, "jsprim": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", - "dev": true, "requires": { "assert-plus": "1.0.0", "extsprintf": "1.3.0", @@ -1814,14 +1799,12 @@ "mime-db": { "version": "1.30.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.30.0.tgz", - "integrity": "sha1-dMZD2i3Z1qRTmZY0ZbJtXKfXHwE=", - "dev": true + "integrity": "sha1-dMZD2i3Z1qRTmZY0ZbJtXKfXHwE=" }, "mime-types": { "version": "2.1.17", "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.17.tgz", "integrity": "sha1-Cdejk/A+mVp5+K+Fe3Cp4KsWVXo=", - "dev": true, "requires": { "mime-db": "1.30.0" } @@ -1888,6 +1871,35 @@ "lodash.toarray": "4.4.0" } }, + "node-fetch": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-1.7.3.tgz", + "integrity": "sha512-NhZ4CsKx7cYm2vSrBAr2PvFOe6sWDf0UYLRqA6svUYg7+/TSfVAu49jYC4BvQ4Sms9SZgdqGBgroqfDhJdTyKQ==", + "requires": { + "encoding": "0.1.12", + "is-stream": "1.1.0" + } + }, + "node-localstorage": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/node-localstorage/-/node-localstorage-1.3.0.tgz", + "integrity": "sha1-LkNqro3Mms6XtDxlwWwNV3vgpVw=", + "requires": { + "write-file-atomic": "1.3.4" + }, + "dependencies": { + "write-file-atomic": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-1.3.4.tgz", + "integrity": "sha1-+Aek8LHZ6ROuekgRLmzDrxmRtF8=", + "requires": { + "graceful-fs": "4.1.11", + "imurmurhash": "0.1.4", + "slide": "1.1.6" + } + } + } + }, "noop-logger": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/noop-logger/-/noop-logger-0.1.1.tgz", @@ -1951,8 +1963,7 @@ "oauth-sign": { "version": "0.8.2", "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.8.2.tgz", - "integrity": "sha1-Rqarfwrq2N6unsBWV4C31O/rnUM=", - "dev": true + "integrity": "sha1-Rqarfwrq2N6unsBWV4C31O/rnUM=" }, "object-assign": { "version": "4.1.1", @@ -1974,6 +1985,15 @@ "wrappy": "1.0.2" } }, + "openpgp": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/openpgp/-/openpgp-2.6.2.tgz", + "integrity": "sha512-Bpgf4Dx5BKJMI0z47j4Bga4opC+oUU935MlFyNS/KZfh4gVGUGCR+P1y/22EtlgtfP9Rw6EHnpVJZ9xC+iOURg==", + "requires": { + "node-fetch": "1.7.3", + "node-localstorage": "1.3.0" + } + }, "os-homedir": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", @@ -2096,8 +2116,7 @@ "performance-now": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", - "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=", - "dev": true + "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=" }, "pidusage": { "version": "1.1.6", @@ -2234,14 +2253,12 @@ "punycode": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", - "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=", - "dev": true + "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=" }, "qs": { "version": "6.5.1", "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.1.tgz", - "integrity": "sha512-eRzhrN1WSINYCDCbrz796z37LOe3m5tmW7RQf6oBntukAG1nmovJvhnwHHRMAfeoItc1m2Hk02WER2aQ/iqs+A==", - "dev": true + "integrity": "sha512-eRzhrN1WSINYCDCbrz796z37LOe3m5tmW7RQf6oBntukAG1nmovJvhnwHHRMAfeoItc1m2Hk02WER2aQ/iqs+A==" }, "rabin-bindings": { "version": "1.7.3", @@ -2393,7 +2410,6 @@ "version": "2.83.0", "resolved": "https://registry.npmjs.org/request/-/request-2.83.0.tgz", "integrity": "sha512-lR3gD69osqm6EYLk9wB/G1W/laGWjzH90t1vEa2xuxHD5KUrSzp9pUSfTm+YC5Nxt2T8nMPEvKlhbQayU7bgFw==", - "dev": true, "requires": { "aws-sign2": "0.7.0", "aws4": "1.6.0", @@ -2443,8 +2459,7 @@ "safe-buffer": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", - "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==", - "dev": true + "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==" }, "sanitize-filename": { "version": "1.6.1", @@ -2531,11 +2546,15 @@ "string-width": "1.0.2" } }, + "slide": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/slide/-/slide-1.1.6.tgz", + "integrity": "sha1-VusCfWW00tzmyy4tMsTUr8nh1wc=" + }, "sntp": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/sntp/-/sntp-2.0.2.tgz", - "integrity": "sha1-UGQRDwr4X3z9t9a2ekACjOUrSys=", - "dev": true, + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/sntp/-/sntp-2.1.0.tgz", + "integrity": "sha512-FL1b58BDrqS3A11lJ0zEdnJ3UOKqVxawAkF3k7F0CVN7VQ34aZrV+G8BZ1WC9ZL7NyrwsW0oviwsWDgRuVYtJg==", "requires": { "hoek": "4.2.0" } @@ -2592,7 +2611,6 @@ "version": "1.13.1", "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.13.1.tgz", "integrity": "sha1-US322mKHFEMW3EwY/hzx2UBzm+M=", - "dev": true, "requires": { "asn1": "0.2.3", "assert-plus": "1.0.0", @@ -2630,8 +2648,7 @@ "stringstream": { "version": "0.0.5", "resolved": "https://registry.npmjs.org/stringstream/-/stringstream-0.0.5.tgz", - "integrity": "sha1-TkhM1N5aC7vuGORjB3EKioFiGHg=", - "dev": true + "integrity": "sha1-TkhM1N5aC7vuGORjB3EKioFiGHg=" }, "strip-ansi": { "version": "3.0.1", @@ -2817,7 +2834,6 @@ "version": "2.3.3", "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.3.tgz", "integrity": "sha1-C2GKVWW23qkL80JdBNVe3EdadWE=", - "dev": true, "requires": { "punycode": "1.4.1" } @@ -2841,7 +2857,6 @@ "version": "0.6.0", "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", - "dev": true, "requires": { "safe-buffer": "5.1.1" } @@ -2850,7 +2865,6 @@ "version": "0.14.5", "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", - "dev": true, "optional": true }, "typedarray": { @@ -2921,8 +2935,7 @@ "uuid": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.1.0.tgz", - "integrity": "sha512-DIWtzUkw04M4k3bf1IcpS2tngXEL26YUD2M0tMDUpnUrz2hgzUBlD55a4FjdLGPvfHxS6uluGWvaVEqgBcVa+g==", - "dev": true + "integrity": "sha512-DIWtzUkw04M4k3bf1IcpS2tngXEL26YUD2M0tMDUpnUrz2hgzUBlD55a4FjdLGPvfHxS6uluGWvaVEqgBcVa+g==" }, "validate-npm-package-license": { "version": "3.0.1", @@ -2938,7 +2951,6 @@ "version": "1.10.0", "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", - "dev": true, "requires": { "assert-plus": "1.0.0", "core-util-is": "1.0.2", diff --git a/package.json b/package.json index 2d8eafa9..b83a42e0 100644 --- a/package.json +++ b/package.json @@ -35,7 +35,9 @@ "glob": "^7.1.2", "i18next": "^8.4.2", "i18next-sync-fs-backend": "^0.1.0", - "pidusage": "^1.1.5" + "openpgp": "^2.6.2", + "pidusage": "^1.1.5", + "request": "^2.83.0" }, "devDependencies": { "electron": "1.7.9", @@ -53,7 +55,6 @@ ], "appId": "com.iotatoken.wallet", "extraResources": [ - "iri/iri.jar", "ccurl/**/*", "ui/**/*", "locales/**/*" diff --git a/package.testnet.json b/package.testnet.json index 0aa30bd9..93f50cb4 100644 --- a/package.testnet.json +++ b/package.testnet.json @@ -28,13 +28,16 @@ "url": "https://github.com/iotaledger/wallet" }, "dependencies": { + "core-util-is": "^1.0.2", "curl.lib.js": "^1.0.22", "ffi": "^2.2.0", "fs-extra": "^1.0.0", "glob": "^7.1.2", "i18next": "^8.4.2", "i18next-sync-fs-backend": "^0.1.0", - "pidusage": "^1.1.5" + "openpgp": "^2.6.2", + "pidusage": "^1.1.5", + "request": "^2.83.0" }, "devDependencies": { "electron": "1.7.4", @@ -52,7 +55,6 @@ ], "appId": "com.iotatoken.wallet-testnet", "extraResources": [ - "iri/iri-testnet.jar", "ccurl/**/*", "ui/**/*", "locales/**/*" diff --git a/ui/js/ui.recover.js b/ui/js/ui.recover.js index d93bafc1..a5380e6e 100644 --- a/ui/js/ui.recover.js +++ b/ui/js/ui.recover.js @@ -185,9 +185,13 @@ WHGl/N/YlZ/p38kb7ZXtuRca7VUPxRzqv3FrUBg recoveryNextBtn.loadingReset('recovery_next') return } - filterSpentAddresses([{address: newAddress}]) + const newAddressesToCheck = [newAddress] + for (let i = 1; i < 50; i++) { + newAddressesToCheck.push(iota.api._newAddress(newSeed, i, 2, false)) + } + filterSpentAddresses(newAddressesToCheck) .then(unspentAddresses => { - if (unspentAddresses.length === 0) { + if (unspentAddresses.length !== newAddressesToCheck.length) { UI.formError('recover', 'sent_to_key_reuse_error', {initial: 'recovery_next'}) $('.remodal-close').off('click') recoveryNextBtn.loadingReset('recovery_next') @@ -650,55 +654,5 @@ WHGl/N/YlZ/p38kb7ZXtuRca7VUPxRzqv3FrUBg return trits } - function filterSpentAddresses (inputs) { - return new Promise((resolve, reject) => { - iota.api.findTransactionObjects({addresses: inputs.map(input => iota.utils.noChecksum(input.address))}, (err, txs) => { - if (err) { - reject(err) - } - txs = txs.filter(tx => tx.value < 0) - if (txs.length > 0) { - var bundles = txs.map(tx => tx.bundle) - iota.api.findTransactionObjects({bundles: bundles}, (err, txs) => { - if (err) { - reject(err) - } - var hashes = txs.filter(tx => tx.currentIndex === 0) - hashes = hashes.map(tx => tx.hash) - iota.api.getLatestInclusion(hashes, (err, states) => { - if (err) { - reject(err) - } - var confirmedHashes = hashes.filter((hash, i) => states[i]) - var unconfirmedHashes = hashes.filter(hash => confirmedHashes.indexOf(hash) === -1).map(hash => { - return { hash: hash, validate: true } - }) - var getBundles = confirmedHashes.concat(unconfirmedHashes).map(hash => new Promise((resolve, reject) => { - iota.api.traverseBundle(typeof hash === 'string' ? hash : hash.hash, null, [], (err, bundle) => { - if (err) { - reject(err) - } - resolve(typeof hash === 'string' ? bundle : {bundle: bundle, validate: true}) - }) - })) - resolve(Promise.all(getBundles).then(bundles => { - bundles = bundles.filter(bundle => { - if (bundle.validate) { - return iota.utils.isBundle(bundle.bundle) - } - return true - }).map(bundle => bundle.hasOwnProperty('validate') ? bundle.bundle : bundle) - var blacklist = bundles.reduce((a, b) => a.concat(b), []).filter(tx => tx.value < 0).map(tx => tx.address) - return inputs.filter(input => blacklist.indexOf(input.address) === -1) - }).catch(err => reject(err))) - }) - }) - } else { - resolve(inputs) - } - }) - }) - } - return UI }(UI || {}, jQuery)) diff --git a/ui/js/ui.transfers.js b/ui/js/ui.transfers.js index cfcfd351..bded3d3e 100644 --- a/ui/js/ui.transfers.js +++ b/ui/js/ui.transfers.js @@ -82,7 +82,7 @@ var UI = (function(UI, $, undefined) { } var transfers = [{"address": address, "value": amount, "message": "", "tag": tag}]; - var outputsToCheck = transfers.map(transfer => { return {address: iota.utils.noChecksum(transfer.address)}}); + var outputsToCheck = transfers.map(transfer => iota.utils.noChecksum(transfer.address)); var exptectedOutputsLength = outputsToCheck.length; filterSpentAddresses(outputsToCheck).then(filtered => { if (filtered.length !== exptectedOutputsLength) { @@ -123,8 +123,11 @@ var UI = (function(UI, $, undefined) { $stack.removeClass("loading"); }); }); + }).catch(err => { + UI.formError('transfer', err, {initial: 'send_it_now'}) + $stack.removeClass("loading") }); - }); + }) }); $("#key-reuse-close-btn").on("click", function(e) { @@ -213,59 +216,16 @@ var UI = (function(UI, $, undefined) { }(UI || {}, jQuery)); -function filterSpentAddresses(inputs) { +function filterSpentAddresses (addresses) { return new Promise((resolve, reject) => { - iota.api.findTransactionObjects({addresses: inputs.map(input => input.address)}, (err, txs) => { - if (err) { - reject(err) - } - txs = txs.filter(tx => tx.value < 0) - if (txs.length > 0) { - var bundles = txs.map(tx => tx.bundle) - iota.api.findTransactionObjects({bundles: bundles}, (err, txs) => { - if (err) { - reject(err) - } - var hashes = txs.filter(tx => tx.currentIndex === 0) - var allBundleHashes = txs.map(tx => tx.bundle) - hashes = hashes.map(tx => tx.hash) - iota.api.getLatestInclusion(hashes, (err, states) => { - if (err) { - reject(err) - } - var confirmedHashes = hashes.filter((hash, i) => states[i]) - var unconfirmedHashes = hashes.filter(hash => confirmedHashes.indexOf(hash) === -1).map(hash => { - return { hash: hash, validate: true } - }) - var getBundles = confirmedHashes.concat(unconfirmedHashes).map(hash => new Promise((resolve, reject) => { - iota.api.traverseBundle(typeof hash == 'string' ? hash : hash.hash, null, [], (err, bundle) => { - if (err) { - reject(err) - } - resolve(typeof hash === 'string' ? bundle : {bundle: bundle, validate: true}) - }) - })) - resolve(Promise.all(getBundles).then(bundles => { - bundles = bundles.filter(bundle => { - if (bundle.validate) { - return iota.utils.isBundle(bundle.bundle) - } - return true - }).map(bundle => bundle.hasOwnProperty('validate') ? bundle.bundle : bundle) - var blacklist = bundles.reduce((a, b) => a.concat(b), []).filter(tx => tx.value < 0).map(tx => tx.address) - return inputs.filter(input => blacklist.indexOf(input.address) === -1) - }).catch(err => reject(err))) - }) - }) - } - else { - resolve(inputs); - } - }) + iota.api.wereAddressesSpentFrom( + iota.valid.isArrayOfHashes(addresses) ? addresses : addresses.map(address => address.address), + (err, wereSpent) => err ? reject(err) : resolve(addresses.filter((address, i) => !wereSpent[i])) + ) }) } -function getUnspentInputs(seed, start, threshold, inputs, cb) { +function getUnspentInputs (seed, start, threshold, inputs, cb) { if (arguments.length === 4) { cb = arguments[3] inputs = {inputs: [], totalBalance: 0, allBalance: 0} @@ -283,8 +243,7 @@ function getUnspentInputs(seed, start, threshold, inputs, cb) { var ordered = res.inputs.sort((a, b) => a.keyIndex - b.keyIndex).reverse() var end = ordered[0].keyIndex getUnspentInputs(seed, end + 1, diff, {inputs: inputs.inputs.concat(filtered), totalBalance: inputs.totalBalance + collected, allBalance: inputs.allBalance}, cb) - } - else { + } else { cb(null, {inputs: inputs.inputs.concat(filtered), totalBalance: inputs.totalBalance + collected, allBalance: inputs.allBalance}) } }).catch(err => cb(err, inputs))