From a26fbfff36f92b9269604f8bd6f26e1aea3e39fa Mon Sep 17 00:00:00 2001 From: emizzle Date: Fri, 25 May 2018 17:13:57 +1000 Subject: [PATCH 1/3] Storage config improvements start. Adjusted the config and have started to support those improvements through the codebase. Currently stuck on starting multiple storage servcies at once. Might need a change in storage config spec. WIP. --- js/embark.js | 15 ++++++++ lib/core/config.js | 14 ++++---- lib/core/engine.js | 10 ++---- lib/i18n/locales/en.json | 7 +++- lib/index.js | 22 +++++------- lib/modules/ipfs/embarkjs.js | 29 +++++++++++++-- lib/modules/ipfs/index.js | 39 ++++++++++++-------- lib/modules/swarm/embarkjs.js | 33 +++++++++++++---- lib/modules/swarm/index.js | 31 +++++++++------- lib/utils/utils.js | 12 +++++++ lib/versions/library_manager.js | 4 ++- package-lock.json | 50 ++++++++++++++++---------- package.json | 1 + templates/boilerplate/embark.json | 3 +- templates/demo/embark.json | 3 +- test_apps/test_app/config/storage.json | 42 ++++++++++++++-------- test_apps/test_app/embark.json | 3 +- 17 files changed, 220 insertions(+), 98 deletions(-) diff --git a/js/embark.js b/js/embark.js index 1d2fec12..87b1e8cf 100644 --- a/js/embark.js +++ b/js/embark.js @@ -230,7 +230,22 @@ EmbarkJS.Storage.setProvider = function(provider, options) { return providerObj.setProvider(options); }; +EmbarkJS.Storage.setProviders = function(provider, dappConnOptions) { + let providerObj = this.Providers[provider]; + + if (!providerObj) { + throw new Error('Unknown storage provider'); + } + + this.currentStorage = providerObj; + + return providerObj.setProviders(dappConnOptions); +}; + EmbarkJS.Storage.isAvailable = function(){ + if (!this.currentStorage) { + throw new Error('Storage provider not set; e.g EmbarkJS.Storage.setProvider("ipfs")'); + } return this.currentStorage.isAvailable(); }; diff --git a/lib/core/config.js b/lib/core/config.js index e150397c..5f66d6b8 100644 --- a/lib/core/config.js +++ b/lib/core/config.js @@ -207,7 +207,7 @@ Config.prototype.loadExternalContractsFiles = function() { }; Config.prototype.loadStorageConfigFile = function() { - var versions = utils.recursiveMerge({"ipfs-api": "17.2.4"}, this.embarkConfig.versions || {}); + var versions = utils.recursiveMerge({"ipfs-api": "17.2.4", "p-iteration": "1.1.7"}, this.embarkConfig.versions || {}); var configObject = { "default": { @@ -215,11 +215,13 @@ Config.prototype.loadStorageConfigFile = function() { "enabled": true, "available_providers": ["ipfs", "swarm"], "ipfs_bin": "ipfs", - "provider": "ipfs", - "protocol": "http", - "host": "localhost", - "port": 5001, - "getUrl": "http://localhost:8080/ipfs/" + "upload": { + "provider": "ipfs", + "protocol": "http", + "host": "localhost", + "port": 5001, + "getUrl": "http://localhost:8080/ipfs/" + } } }; diff --git a/lib/core/engine.js b/lib/core/engine.js index 37ca5c0e..7cf14908 100644 --- a/lib/core/engine.js +++ b/lib/core/engine.js @@ -105,10 +105,9 @@ class Engine { "deployment": this.deploymentService, "fileWatcher": this.fileWatchService, "webServer": this.webServerService, - "ipfs": this.ipfsService, "web3": this.web3Service, "libraryManager": this.libraryManagerService, - "swarm": this.swarmService + "storage": this.storageService }; let service = services[serviceName]; @@ -133,8 +132,8 @@ class Engine { logger: this.logger, plugins: this.plugins }); - this.events.on('code-generator-ready', function () { + console.log('CODE GENERATOR READY EVENT FIRED'); self.events.request('code', function (abi, contractsJSON) { pipeline.build(abi, contractsJSON, null, () => { if (self.watch) { @@ -263,16 +262,13 @@ class Engine { }); } - ipfsService(_options) { + storageService(_options) { this.registerModule('ipfs', { addCheck: this.servicesMonitor.addCheck.bind(this.servicesMonitor), storageConfig: this.config.storageConfig, host: _options.host, port: _options.port }); - } - - swarmService(_options) { this.registerModule('swarm', { addCheck: this.servicesMonitor.addCheck.bind(this.servicesMonitor), storageConfig: this.config.storageConfig, diff --git a/lib/i18n/locales/en.json b/lib/i18n/locales/en.json index f23f3a21..33b0e605 100644 --- a/lib/i18n/locales/en.json +++ b/lib/i18n/locales/en.json @@ -127,5 +127,10 @@ "Cannot upload: {{platform}} node is not running on {{protocol}}://{{host}}:{{port}}.": "Cannot upload: {{platform}} node is not running on {{protocol}}://{{host}}:{{port}}.", "Error while downloading the file": "Error while downloading the file", "Error while loading the content of ": "Error while loading the content of ", - "no contracts found": "no contracts found" + "no contracts found": "no contracts found", + "IPFS node is offline": "IPFS node is offline", + "IPFS node detected": "IPFS node detected", + "Webserver is offline": "Webserver is offline", + "DApp path length is too long: \"": "DApp path length is too long: \"", + "This is known to cause issues with some applications, please consider reducing your DApp path's length to 66 characters or less.": "This is known to cause issues with some applications, please consider reducing your DApp path's length to 66 characters or less." } \ No newline at end of file diff --git a/lib/index.js b/lib/index.js index a4a809e1..354947b6 100644 --- a/lib/index.js +++ b/lib/index.js @@ -122,10 +122,9 @@ class Embark { engine.startService("web3"); engine.startService("pipeline"); engine.startService("deployment"); + engine.startService('storage'); engine.startService("codeGenerator"); - // TODO: this should be just 'storage' and the storage should figure out the module - engine.startService(engine.config.storageConfig.provider); - + engine.events.on('check:backOnline:Ethereum', function () { engine.logger.info(__('Ethereum node detected') + '..'); engine.config.reloadConfig(); @@ -198,10 +197,8 @@ class Embark { engine.startService("web3"); engine.startService("pipeline"); engine.startService("deployment", {onlyCompile: options.onlyCompile}); + engine.startService("storage"); engine.startService("codeGenerator"); - // TODO: this should be just 'storage' and the storage should figure out the modules to load - engine.startService("ipfs"); - engine.startService("swarm"); callback(); }, function deploy(callback) { @@ -254,8 +251,8 @@ class Embark { engine.startService("libraryManager"); engine.startService("pipeline"); engine.startService("deployment", {onlyCompile: true}); + engine.startService("codeGenerator"); - engine.deployManager.deployContracts(function (err) { callback(err); }); @@ -304,7 +301,7 @@ class Embark { }); engine.init(); - let platform = engine.config.storageConfig.provider; + let platform = engine.config.storageConfig.upload.provider; let cmdPlugin; async.waterfall([ @@ -316,9 +313,8 @@ class Embark { engine.startService("web3"); engine.startService("pipeline"); engine.startService("deployment"); + engine.startService('storage'); engine.startService("codeGenerator"); - // TODO: this should be just 'storage' and the storage should figure out the modules to load - engine.startService(platform.toLowerCase()); engine.startMonitor(); callback(); }, @@ -335,7 +331,8 @@ class Embark { } checkFn.fn(function (serviceCheckResult) { if (!serviceCheckResult.status || serviceCheckResult.status === 'off') { - return callback({message: __('Cannot upload: {{platform}} node is not running on {{protocol}}://{{host}}:{{port}}.', {platform: platform, protocol: engine.config.storageConfig.protocol, host: engine.config.storageConfig.host, port: engine.config.storageConfig.port})}); + let config = engine.config.storageConfig.upload; + return callback({message: __('Cannot upload: {{platform}} node is not running on {{protocol}}://{{host}}:{{port}}.', {platform: platform, protocol: config.protocol, host: config.host, port: config.port})}); } callback(); }); @@ -357,8 +354,7 @@ class Embark { }); } if (!cmdPlugin) { - engine.logger.info(__('try "{{ipfs}}" or "{{swarm}}"', {ipfs: 'embark upload ipfs', swarm: 'embark upload swarm'}).green); - return callback({message: 'unknown platform: ' + platform}); + return callback({message: __('platform "{{platform}}" is specified as the upload provider, however no plugins have registered an upload command for "{{platform}}".', {platform: platform})}); } callback(); }, diff --git a/lib/modules/ipfs/embarkjs.js b/lib/modules/ipfs/embarkjs.js index 1c374cb5..b90ca176 100644 --- a/lib/modules/ipfs/embarkjs.js +++ b/lib/modules/ipfs/embarkjs.js @@ -1,4 +1,5 @@ import IpfsApi from 'ipfs-api'; +//import {some} from 'p-iteration'; let __embarkIPFS = {}; @@ -10,7 +11,7 @@ __embarkIPFS.setProvider = function (options) { self.ipfsConnection = IpfsApi('localhost', '5001'); self._getUrl = "http://localhost:8080/ipfs/"; } else { - var ipfsOptions = {host: options.server, protocol: 'http'}; + var ipfsOptions = {host: options.host || options.server, protocol: 'http'}; if (options.protocol) { ipfsOptions.protocol = options.protocol; } @@ -22,7 +23,7 @@ __embarkIPFS.setProvider = function (options) { } resolve(self); } catch (err) { - console.log(err); + console.error(err); self.ipfsConnection = null; reject(new Error('Failed to connect to IPFS')); } @@ -30,6 +31,29 @@ __embarkIPFS.setProvider = function (options) { return promise; }; +__embarkIPFS.setProviders = async function (dappConnOptions) { + var self = this; + try { + let workingConnFound = await some(dappConnOptions, async (dappConn) => { + if(dappConn === '$BZZ' || dappConn.provider !== 'ipfs') return false; // swarm has no bearing for ipfs plugin, continue + else { + // set the provider then check the connection, if true, use that provider, else, check next provider + try{ + await self.setProvider(dappConn); + return await self.isAvailable(); + } catch(err) { + return false; + } + } + }); + if(!workingConnFound) throw new Error('Could not connect to IPFS using any of the dappConnections in the storage config'); + else return self; + } catch (err) { + self.ipfsConnection = null; + throw new Error('Failed to connect to IPFS: ' + err.message); + } +}; + __embarkIPFS.saveText = function (text) { const self = this; var promise = new Promise(function (resolve, reject) { @@ -119,3 +143,4 @@ __embarkIPFS.getUrl = function (hash) { return (this._getUrl || "http://localhost:8080/ipfs/") + hash; }; + diff --git a/lib/modules/ipfs/index.js b/lib/modules/ipfs/index.js index bccd839c..234fc33c 100644 --- a/lib/modules/ipfs/index.js +++ b/lib/modules/ipfs/index.js @@ -3,6 +3,7 @@ let utils = require('../../utils/utils.js'); let fs = require('../../core/fs.js'); let RunCode = require('../../coderunner/runCode'); let IpfsApi = require('ipfs-api'); +const _ = require('underscore'); class IPFS { @@ -11,8 +12,9 @@ class IPFS { this.events = embark.events; this.buildDir = options.buildDir; this.storageConfig = options.storageConfig; - this.host = options.host || this.storageConfig.host; - this.port = options.port || this.storageConfig.port; + this.host = options.host || this.storageConfig.upload.host; + this.port = options.port || this.storageConfig.upload.port; + this.protocol = options.protocol || this.storageConfig.upload.protocol; this.addCheck = options.addCheck; this.embark = embark; @@ -41,7 +43,7 @@ class IPFS { if (!storageConfig.enabled) { return; } - if (storageConfig.provider !== 'ipfs' && storageConfig.available_providers.indexOf("ipfs") < 0) { + if (storageConfig.upload.provider !== 'ipfs' || storageConfig.available_providers.indexOf("ipfs") < 0) { return; } @@ -59,7 +61,13 @@ class IPFS { self.addCheck('IPFS', function (cb) { self.logger.trace("Checking IPFS version..."); - utils.httpGetJson('http://' + self.host + ':' + self.port + '/api/v0/version', function (err, body) { + let url = (self.protocol || 'http') + '://' + self.host + ':' + self.port + '/api/v0/version'; + if(self.protocol !== 'https'){ + utils.httpGetJson(url, versionCb); + } else { + utils.httpsGetJson(url, versionCb); + } + function versionCb(err, body) { if (err) { self.logger.trace("Check IPFS version error: " + err); return cb({name: "IPFS ", status: 'off'}); @@ -68,7 +76,7 @@ class IPFS { return cb({name: ("IPFS " + body.Version), status: 'on'}); } return cb({name: "IPFS ", status: 'on'}); - }); + } }); } @@ -79,7 +87,7 @@ class IPFS { return; } - if ((this.storageConfig.available_providers.indexOf('ipfs') < 0) && (this.storageConfig.provider !== 'ipfs' || this.storageConfig.enabled !== true)) { + if (this.storageConfig.available_providers.indexOf('ipfs') < 0 || _.findWhere(this.storageConfig.dappConnection, {'provider': 'ipfs'}) === undefined || this.storageConfig.enabled !== true) { return; } @@ -92,6 +100,15 @@ class IPFS { } }); + self.events.request("version:get:p-iteration", function(pIterationVersion) { + let currentPIterationVersion = require('../../../package.json').dependencies["p-iteration"]; + if (pIterationVersion !== currentPIterationVersion) { + self.events.request("version:getPackageLocation", "p-iteration", pIterationVersion, function(err, location) { + self.embark.registerImportFile("p-iteration", fs.dappPath(location)); + }); + } + }); + let code = ""; code += "\n" + fs.readFileSync(utils.joinPath(__dirname, 'embarkjs.js')).toString(); code += "\nEmbarkJS.Storage.registerProvider('ipfs', __embarkIPFS);"; @@ -100,16 +117,10 @@ class IPFS { } addSetProvider() { - let config = JSON.stringify({ - server: this.storageConfig.host, - port: this.storageConfig.port, - protocol: this.storageConfig.protocol, - getUrl: this.storageConfig.getUrl - }); - let code = "\nEmbarkJS.Storage.setProvider('ipfs'," + config + ");"; + let code = "\nEmbarkJS.Storage.setProviders('ipfs'," + JSON.stringify(this.storageConfig.dappConnection) + ");"; let shouldInit = (storageConfig) => { - return (storageConfig.provider === 'ipfs' && storageConfig.enabled === true); + return (this.storageConfig.dappConnection !== undefined && this.storageConfig.dappConnection.some((dappConn) => dappConn.provider === 'ipfs') && storageConfig.enabled === true); }; this.embark.addProviderInit('storage', code, shouldInit); diff --git a/lib/modules/swarm/embarkjs.js b/lib/modules/swarm/embarkjs.js index 9a415258..78d163ec 100644 --- a/lib/modules/swarm/embarkjs.js +++ b/lib/modules/swarm/embarkjs.js @@ -1,20 +1,19 @@ /*global web3 */ let __embarkSwarm = {}; const bytes = require("eth-lib/lib/bytes"); +import {some} from 'p-iteration'; __embarkSwarm.setProvider = function (options) { this.bzz = web3.bzz; - this.protocol = options.protocol; - this.host = options.host; - this.port = options.port; - this.connectUrl = `${options.protocol}://${options.host}:${options.port}`; + this.protocol = options.protocol || 'http'; + this.connectUrl = `${this.protocol}://${options.host}:${options.port}`; this.connectError = new Error(`Cannot connect to Swarm node on ${this.connectUrl}`); - this._getUrl = options.getUrl || `${this.connectUrl}/bzzr:/`; + //this._getUrl = options.getUrl || `${this.connectUrl}/bzzr:/`; return new Promise((resolve, reject) => { try { if (!this.bzz.currentProvider) { - this.bzz.setProvider(`${options.protocol}://${options.host}:${options.port}`); + this.bzz.setProvider(this.connectUrl); } resolve(this); } catch (err) { @@ -24,6 +23,28 @@ __embarkSwarm.setProvider = function (options) { }); }; +__embarkSwarm.setProviders = async function (dappConnOptions) { + var self = this; + try { + let workingConnFound = await some(dappConnOptions, async (dappConn) => { + if(dappConn === '$BZZ'){ + return self.isAvailable(); + } + else if(dappConn.provider === 'swarm') + { + // set the provider then check the connection, if true, use that provider, else, check next provider + await self.setProvider(dappConn); + return self.isAvailable(); + } + else return false; + }); + if(!workingConnFound) throw new Error('Could not connect to Swarm using any of the dappConnections in the storage config'); + else return self; + } catch (err) { + throw new Error('Failed to connect to Swarm: ' + err.message); + } +}; + __embarkSwarm.isAvailable = function () { return new Promise((resolve, reject) => { if (!this.bzz) { diff --git a/lib/modules/swarm/index.js b/lib/modules/swarm/index.js index a8901cc1..f336c0f7 100644 --- a/lib/modules/swarm/index.js +++ b/lib/modules/swarm/index.js @@ -9,8 +9,9 @@ class Swarm { this.events = embark.events; this.buildDir = options.buildDir; this.storageConfig = options.storageConfig; - this.host = options.host || this.storageConfig.host; - this.port = options.port || this.storageConfig.port; + this.host = options.host || options.storageConfig.upload.host; + this.port = options.port || options.storageConfig.upload.port; + this.protocol = options.protocol || options.storageConfig.upload.protocol; this.addCheck = options.addCheck; this.embark = embark; this.bzz = options.bzz; @@ -24,7 +25,7 @@ class Swarm { initSwarmProvider(){ if(!this.bzz.currentProvider) { - this.bzz.setProvider(`http://${this.host}:${this.port}`); + this.bzz.setProvider(`${this.protocol}://${this.host}:${this.port}`); } } @@ -47,7 +48,7 @@ class Swarm { if (!storageConfig.enabled) { return; } - if (storageConfig.provider !== 'swarm' && storageConfig.available_providers.indexOf("swarm") < 0) { + if (storageConfig.upload.provider !== 'swarm' || storageConfig.available_providers.indexOf("swarm") < 0) { return; } @@ -76,15 +77,25 @@ class Swarm { } addSwarmToEmbarkJS() { + let self = this; // TODO: make this a shouldAdd condition if (this.storageConfig === {}) { return; } - if ((this.storageConfig.available_providers.indexOf('swarm') < 0) && (this.storageConfig.provider !== 'swarm' || this.storageConfig.enabled !== true)) { + if (this.storageConfig.available_providers.indexOf('swarm') < 0 || this.storageConfig.enabled !== true) { return; } + this.events.request("version:get:p-iteration", function(pIterationVersion) { + let currentPIterationVersion = require('../../../package.json').dependencies["p-iteration"]; + if (pIterationVersion !== currentPIterationVersion) { + self.events.request("version:getPackageLocation", "p-iteration", pIterationVersion, function(err, location) { + self.embark.registerImportFile("p-iteration", fs.dappPath(location)); + }); + } + }); + let code = ""; code += "\n" + fs.readFileSync(utils.joinPath(__dirname, 'embarkjs.js')).toString(); code += "\nEmbarkJS.Storage.registerProvider('swarm', __embarkSwarm);"; @@ -93,16 +104,10 @@ class Swarm { } addSetProvider() { - let config = JSON.stringify({ - host: this.storageConfig.host, - port: this.storageConfig.port, - protocol: this.storageConfig.protocol, - getUrl: this.storageConfig.getUrl - }); - let code = "\nEmbarkJS.Storage.setProvider('swarm'," + config + ");"; + let code = "\nEmbarkJS.Storage.setProviders('swarm'," + JSON.stringify(this.storageConfig.dappConnection) + ");"; let shouldInit = (storageConfig) => { - return (storageConfig.provider === 'swarm' && storageConfig.enabled === true); + return (this.storageConfig.dappConnection !== undefined && this.storageConfig.dappConnection.some((dappConn) => dappConn.provider === 'swarm') && storageConfig.enabled === true); }; this.embark.addProviderInit('storage', code, shouldInit); diff --git a/lib/utils/utils.js b/lib/utils/utils.js index f9b0c244..ea3e1a3e 100644 --- a/lib/utils/utils.js +++ b/lib/utils/utils.js @@ -69,6 +69,17 @@ function httpGetJson(url, callback) { }); } +function httpsGetJson(url, callback) { + httpGetRequest(https, url, function(err, body) { + try { + let parsed = JSON.parse(body); + return callback(err, parsed); + } catch(e) { + return callback(e); + } + }); +} + function runCmd(cmd, options) { let result = shelljs.exec(cmd, options || {silent: true}); if (result.code !== 0) { @@ -208,6 +219,7 @@ module.exports = { httpGet: httpGet, httpsGet: httpsGet, httpGetJson: httpGetJson, + httpsGetJson: httpsGetJson, runCmd: runCmd, cd: cd, sed: sed, diff --git a/lib/versions/library_manager.js b/lib/versions/library_manager.js index 3eb1f72b..66527767 100644 --- a/lib/versions/library_manager.js +++ b/lib/versions/library_manager.js @@ -23,15 +23,17 @@ class LibraryManager { let solcVersionInConfig = this.contractsConfig.versions.solc; let web3VersionInConfig = this.contractsConfig.versions["web3"]; let ipfsApiVersion = this.storageConfig.versions["ipfs-api"]; + let pIterationVersion = this.storageConfig.versions["p-iteration"]; this.versions['solc'] = solcVersionInConfig; this.versions['web3'] = web3VersionInConfig; this.versions['ipfs-api'] = ipfsApiVersion; + this.versions['p-iteration'] = pIterationVersion; Object.keys(this.versions).forEach(versionKey => { const newVersion = this.versions[versionKey].trim(); if (newVersion !== this.versions[versionKey]) { - this.embark.logger.warn(__('There a a space in the version of {{versionKey}}. We corrected it for you ({{correction}}).', {versionKey: versionKey, correction: `"${this.versions[versionKey]}" => "${newVersion}"`})); + this.embark.logger.warn(__('There is a space in the version of {{versionKey}}. We corrected it for you ({{correction}}).', {versionKey: versionKey, correction: `"${this.versions[versionKey]}" => "${newVersion}"`})); this.versions[versionKey] = newVersion; } }); diff --git a/package-lock.json b/package-lock.json index b22d565f..10c69650 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1434,7 +1434,7 @@ "browserify-zlib": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.2.0.tgz", - "integrity": "sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA==", + "integrity": "sha1-KGlFnZqjviRf6P4sofRuLn9U1z8=", "requires": { "pako": "1.0.6" } @@ -2466,7 +2466,7 @@ "domain-browser": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-1.2.0.tgz", - "integrity": "sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA==" + "integrity": "sha1-PTH1AZGmdJ3RN1p/Ui6CPULlTto=" }, "drbg.js": { "version": "1.0.1", @@ -2582,7 +2582,7 @@ "errno": { "version": "0.1.7", "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.7.tgz", - "integrity": "sha512-MfrRBDWzIWifgq6tJj60gkAwtLNb6sQPlcFrSOflcP1aFmmruKQ2wRnze/8V6kgyz7H3FF8Npzv78mZ7XLLflg==", + "integrity": "sha1-RoTXF3mtOa8Xfj8AeZb3xnyFJhg=", "requires": { "prr": "1.0.1" } @@ -3243,7 +3243,7 @@ "resolved": "https://registry.npmjs.org/ethereumjs-testrpc/-/ethereumjs-testrpc-6.0.3.tgz", "integrity": "sha1-eguHvzZw+S9gf5j6aniAHZdBsSQ=", "requires": { - "webpack": "3.12.0" + "webpack": "3.11.0" } }, "ethereumjs-tx": { @@ -6042,7 +6042,7 @@ "json-loader": { "version": "0.5.7", "resolved": "https://registry.npmjs.org/json-loader/-/json-loader-0.5.7.tgz", - "integrity": "sha512-QLPs8Dj7lnf3e3QYS1zkCo+4ZwqOiF9d/nZnYozTISxXWCfNs9yuky5rJw4/W34s7POaNlbZmQGaB5NiXCbP4w==" + "integrity": "sha1-3KFKcCNf+C8KyaOr62DTN6NlGF0=" }, "json-parse-better-errors": { "version": "1.0.2", @@ -6470,7 +6470,7 @@ "lockfile": "1.0.4", "node-fetch": "2.1.2", "semver": "5.5.0", - "tar": "4.4.2", + "tar": "4.4.4", "url-join": "4.0.0" }, "dependencies": { @@ -6500,14 +6500,23 @@ "graceful-fs": "4.1.11" } }, + "minipass": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.3.3.tgz", + "integrity": "sha512-/jAn9/tEX4gnpyRATxgHEOV6xbcyxgT7iUnxo9Y3+OB0zX00TgKIv/2FZCf5brBbICcwbLqVv2ImjvWWrQMSYw==", + "requires": { + "safe-buffer": "5.1.2", + "yallist": "3.0.2" + } + }, "tar": { - "version": "4.4.2", - "resolved": "https://registry.npmjs.org/tar/-/tar-4.4.2.tgz", - "integrity": "sha512-BfkE9CciGGgDsATqkikUHrQrraBCO+ke/1f6SFAEMnxyyfN9lxC+nW1NFWMpqH865DhHIy9vQi682gk1X7friw==", + "version": "4.4.4", + "resolved": "https://registry.npmjs.org/tar/-/tar-4.4.4.tgz", + "integrity": "sha512-mq9ixIYfNF9SK0IS/h2HKMu8Q2iaCuhDDsZhdEag/FHv8fOaYld4vN7ouMgcSSt5WKZzPs8atclTcJm36OTh4w==", "requires": { "chownr": "1.0.1", "fs-minipass": "1.2.5", - "minipass": "2.3.1", + "minipass": "2.3.3", "minizlib": "1.1.0", "mkdirp": "0.5.1", "safe-buffer": "5.1.2", @@ -7684,7 +7693,7 @@ "node-libs-browser": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/node-libs-browser/-/node-libs-browser-2.1.0.tgz", - "integrity": "sha512-5AzFzdoIMb89hBGMZglEegffzgRg+ZFoUmisQ8HI4j1KDdpx13J0taNp2y9xPbur6W61gepGDDotGBVQ7mfUCg==", + "integrity": "sha1-X5QmPUBPbkR2fXJpAf/wVHjWAN8=", "requires": { "assert": "1.4.1", "browserify-zlib": "0.2.0", @@ -8057,7 +8066,7 @@ "os-locale": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-2.1.0.tgz", - "integrity": "sha512-3sslG3zJbEYcaC4YVAvDorjGxc7tv6KVATnLPZONiljsUncvihe9BQoVCEs0RZ1kmf4Hk9OBqlZfJZWI4GanKA==", + "integrity": "sha1-QrwpAKa1uL0XN2yOiCtlr8zyS/I=", "requires": { "execa": "0.7.0", "lcid": "1.0.0", @@ -8079,6 +8088,11 @@ "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=" }, + "p-iteration": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/p-iteration/-/p-iteration-1.1.7.tgz", + "integrity": "sha512-VsYvUPjm2edbKkX4QzlASC1qB2e4Z6IE9WPaRVHKwCtobmB6vfUcU9eBOwj1k5uMNi8O6w89QfsDatO5ePSjQg==" + }, "p-limit": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.2.0.tgz", @@ -8121,7 +8135,7 @@ "pako": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.6.tgz", - "integrity": "sha512-lQe48YPsMJAig+yngZ87Lus+NF+3mtu7DVOBu6b/gHO1YpKwIj5AWjZ/TOS7i46HD/UixzWb1zeWDZfGZ3iYcg==" + "integrity": "sha1-AQEhG6pwxLykoPY/Igbpe3368lg=" }, "parse-asn1": { "version": "5.1.1", @@ -11840,9 +11854,9 @@ "version": "github:dignifiedquire/webcrypto-shim#190bc9ec341375df6025b17ae12ddb2428ea49c8" }, "webpack": { - "version": "3.12.0", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-3.12.0.tgz", - "integrity": "sha512-Sw7MdIIOv/nkzPzee4o0EdvCuPmxT98+vVpIvwtcwcF1Q4SDSNp92vwcKc4REe7NItH9f1S4ra9FuQ7yuYZ8bQ==", + "version": "3.11.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-3.11.0.tgz", + "integrity": "sha1-d9pFGx17SxF62vQaGpO1dC8k2JQ=", "requires": { "acorn": "5.5.3", "acorn-dynamic-import": "2.0.2", @@ -11910,7 +11924,7 @@ "webpack-sources": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.1.0.tgz", - "integrity": "sha512-aqYp18kPphgoO5c/+NaUvEeACtZjMESmDChuD3NBciVpah3XpMEU9VAAtIaB1BsfJWWTSdv8Vv1m3T0aRk2dUw==", + "integrity": "sha1-oQHrrlnWUHNU1x2AE5UKOot6WlQ=", "requires": { "source-list-map": "2.0.0", "source-map": "0.6.1" @@ -11919,7 +11933,7 @@ "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + "integrity": "sha1-dHIq8y6WFOnCh6jQu95IteLxomM=" } } }, diff --git a/package.json b/package.json index 3d341cf0..5fcb6a6f 100644 --- a/package.json +++ b/package.json @@ -54,6 +54,7 @@ "merge": "^1.2.0", "orbit-db": "^0.17.3", "os-locale": "^2.1.0", + "p-iteration": "^1.1.7", "parse-json": "^4.0.0", "promptly": "^2.1.0", "propose": "0.0.5", diff --git a/templates/boilerplate/embark.json b/templates/boilerplate/embark.json index 40c103a5..d0bfeb7a 100644 --- a/templates/boilerplate/embark.json +++ b/templates/boilerplate/embark.json @@ -11,7 +11,8 @@ "versions": { "web3": "1.0.0-beta", "solc": "0.4.23", - "ipfs-api": "17.2.4" + "ipfs-api": "17.2.4", + "p-iteration": "1.1.7" }, "plugins": {} } diff --git a/templates/demo/embark.json b/templates/demo/embark.json index 7bd3d48a..05855ace 100644 --- a/templates/demo/embark.json +++ b/templates/demo/embark.json @@ -10,7 +10,8 @@ "versions": { "web3": "1.0.0-beta", "solc": "0.4.23", - "ipfs-api": "17.2.4" + "ipfs-api": "17.2.4", + "p-iteration": "1.1.7" }, "plugins": { } diff --git a/test_apps/test_app/config/storage.json b/test_apps/test_app/config/storage.json index a226dcb3..6d06e3c7 100644 --- a/test_apps/test_app/config/storage.json +++ b/test_apps/test_app/config/storage.json @@ -1,25 +1,39 @@ { "default": { "enabled": true, - "available_providers": ["ipfs", "swarm"], "ipfs_bin": "ipfs", - "provider": "ipfs", - "host": "localhost", - "port": 5001 + + "available_providers": ["ipfs", "swarm"], + + "upload": { + "provider": "ipfs", + "host": "localhost", + "port": 5001 + }, + + "dappConnection": [ + "$BZZ", + {"provider": "swarm", "host": "localhost", "port": 8500, "getUrl": "http://localhost:8500/bzzr:/"}, + {"provider": "ipfs", "host": "localhost", "port": 5001, "getUrl": "http://localhost:8080/ipfs/"} + ] }, "development": { "enabled": true, - "provider": "swarm", - "host": "swarm-gateways.net", - "port": false, - "getUrl": "http://swarm-gateways.net/bzzr:/" + "upload": { + "provider": "ipfs", + "host": "localhost", + "port": 5001, + "getUrl": "http://localhost:8080/ipfs/" + } }, "livenet": { "enabled": true, - "provider": "ipfs", - "host": "ipfs.infura.io", - "protocol": "https", - "port": false, - "getUrl": "https://ipfs.infura.io/ipfs/" + "upload":{ + "provider": "ipfs", + "host": "ipfs.infura.io", + "protocol": "https", + "port": false, + "getUrl": "https://ipfs.infura.io/ipfs/" + } } -} +} \ No newline at end of file diff --git a/test_apps/test_app/embark.json b/test_apps/test_app/embark.json index 66dfb006..1ae6c162 100644 --- a/test_apps/test_app/embark.json +++ b/test_apps/test_app/embark.json @@ -17,7 +17,8 @@ "versions": { "solc": "0.4.18", "web3": "1.0.0-beta.34", - "ipfs-api": "17.2.7" + "ipfs-api": "17.2.7", + "p-iteration": "1.1.7" }, "plugins": { "embark-service": {} From f9bfe20de1c876412d5fab34ab3eed7c8e43cbbc Mon Sep 17 00:00:00 2001 From: emizzle Date: Mon, 28 May 2018 22:59:18 +1000 Subject: [PATCH 2/3] Added a global storage module that inits ipfs or swarm depending on dappConnection options. WIP. --- lib/core/engine.js | 22 +++++++++------ lib/modules/ipfs/embarkjs.js | 24 ---------------- lib/modules/ipfs/index.js | 28 +++++++++--------- lib/modules/storage/embarkjs.js | 32 +++++++++++++++++++++ lib/modules/storage/index.js | 50 +++++++++++++++++++++++++++++++++ lib/modules/swarm/embarkjs.js | 28 ++---------------- lib/modules/swarm/index.js | 28 +++++++++--------- 7 files changed, 127 insertions(+), 85 deletions(-) create mode 100644 lib/modules/storage/embarkjs.js create mode 100644 lib/modules/storage/index.js diff --git a/lib/core/engine.js b/lib/core/engine.js index 7cf14908..d45aa5bf 100644 --- a/lib/core/engine.js +++ b/lib/core/engine.js @@ -263,19 +263,25 @@ class Engine { } storageService(_options) { - this.registerModule('ipfs', { + this.registerModule('storage', { addCheck: this.servicesMonitor.addCheck.bind(this.servicesMonitor), storageConfig: this.config.storageConfig, host: _options.host, port: _options.port }); - this.registerModule('swarm', { - addCheck: this.servicesMonitor.addCheck.bind(this.servicesMonitor), - storageConfig: this.config.storageConfig, - // TODO: this should not be needed and should be deducted from the config instead - // the eth provider is not necessary the same as the swarm one - bzz: this.blockchain.web3.bzz - }); + // this.registerModule('ipfs', { + // addCheck: this.servicesMonitor.addCheck.bind(this.servicesMonitor), + // storageConfig: this.config.storageConfig, + // host: _options.host, + // port: _options.port + // }); + // this.registerModule('swarm', { + // addCheck: this.servicesMonitor.addCheck.bind(this.servicesMonitor), + // storageConfig: this.config.storageConfig, + // // TODO: this should not be needed and should be deducted from the config instead + // // the eth provider is not necessary the same as the swarm one + // bzz: this.blockchain.web3.bzz + // }); } web3Service(options) { diff --git a/lib/modules/ipfs/embarkjs.js b/lib/modules/ipfs/embarkjs.js index b90ca176..39c849ed 100644 --- a/lib/modules/ipfs/embarkjs.js +++ b/lib/modules/ipfs/embarkjs.js @@ -1,5 +1,4 @@ import IpfsApi from 'ipfs-api'; -//import {some} from 'p-iteration'; let __embarkIPFS = {}; @@ -31,29 +30,6 @@ __embarkIPFS.setProvider = function (options) { return promise; }; -__embarkIPFS.setProviders = async function (dappConnOptions) { - var self = this; - try { - let workingConnFound = await some(dappConnOptions, async (dappConn) => { - if(dappConn === '$BZZ' || dappConn.provider !== 'ipfs') return false; // swarm has no bearing for ipfs plugin, continue - else { - // set the provider then check the connection, if true, use that provider, else, check next provider - try{ - await self.setProvider(dappConn); - return await self.isAvailable(); - } catch(err) { - return false; - } - } - }); - if(!workingConnFound) throw new Error('Could not connect to IPFS using any of the dappConnections in the storage config'); - else return self; - } catch (err) { - self.ipfsConnection = null; - throw new Error('Failed to connect to IPFS: ' + err.message); - } -}; - __embarkIPFS.saveText = function (text) { const self = this; var promise = new Promise(function (resolve, reject) { diff --git a/lib/modules/ipfs/index.js b/lib/modules/ipfs/index.js index 234fc33c..19017ec8 100644 --- a/lib/modules/ipfs/index.js +++ b/lib/modules/ipfs/index.js @@ -18,11 +18,11 @@ class IPFS { this.addCheck = options.addCheck; this.embark = embark; - this.commandlineDeploy(); - this.setServiceCheck(); - this.addIPFSToEmbarkJS(); - this.addSetProvider(); - this.addIpfsObjectToConsole(); + // this.commandlineDeploy(); + // this.setServiceCheck(); + // this.addProviderToEmbarkJS(); + // this.addSetProvider(); + // this.addObjectToConsole(); } commandlineDeploy() { @@ -80,7 +80,7 @@ class IPFS { }); } - addIPFSToEmbarkJS() { + addProviderToEmbarkJS() { const self = this; // TODO: make this a shouldAdd condition if (this.storageConfig === {}) { @@ -116,17 +116,17 @@ class IPFS { this.embark.addCodeToEmbarkJS(code); } - addSetProvider() { - let code = "\nEmbarkJS.Storage.setProviders('ipfs'," + JSON.stringify(this.storageConfig.dappConnection) + ");"; + // addSetProvider() { + // let code = "\nEmbarkJS.Storage.setProviders('ipfs'," + JSON.stringify(this.storageConfig.dappConnection) + ");"; - let shouldInit = (storageConfig) => { - return (this.storageConfig.dappConnection !== undefined && this.storageConfig.dappConnection.some((dappConn) => dappConn.provider === 'ipfs') && storageConfig.enabled === true); - }; + // let shouldInit = (storageConfig) => { + // return (this.storageConfig.dappConnection !== undefined && this.storageConfig.dappConnection.some((dappConn) => dappConn.provider === 'ipfs') && storageConfig.enabled === true); + // }; - this.embark.addProviderInit('storage', code, shouldInit); - } + // this.embark.addProviderInit('storage', code, shouldInit); + // } - addIpfsObjectToConsole() { + addObjectToConsole() { let ipfs = IpfsApi(this.storageConfig.host, this.storageConfig.port); RunCode.doEval("", {ipfs: ipfs}); } diff --git a/lib/modules/storage/embarkjs.js b/lib/modules/storage/embarkjs.js new file mode 100644 index 00000000..dac27481 --- /dev/null +++ b/lib/modules/storage/embarkjs.js @@ -0,0 +1,32 @@ +/* global EmbarkJS */ + +import {findSeries} from 'p-iteration'; + +let __embarkStorage = {}; + +__embarkStorage.setProviders = async function (dappConnOptions) { + var self = this; + try { + let workingConnFound = await findSeries(dappConnOptions, async (dappConn) => { + if(dappConn === '$BZZ' || dappConn.provider === 'swarm'){ + let options = dappConnOptions; + options.useOnlyCurrentProvider = dappConn === '$BZZ'; + await EmbarkJS.Storage.setProvider('swarm', options); + return EmbarkJS.Storage.isAvailable(); + } + else if(dappConn.provider === 'ipfs') { + // set the provider then check the connection, if true, use that provider, else, check next provider + try{ + await EmbarkJS.Storage.setProvider('ipfs', dappConn); + return EmbarkJS.Storage.isAvailable(); + } catch(err) { + return false; + } + } + }); + if(!workingConnFound) throw new Error('Could not connect to a storage provider using any of the dappConnections in the storage config'); + } catch (err) { + self.ipfsConnection = null; + throw new Error('Failed to connect to IPFS: ' + err.message); + } + }; diff --git a/lib/modules/storage/index.js b/lib/modules/storage/index.js new file mode 100644 index 00000000..af67d58c --- /dev/null +++ b/lib/modules/storage/index.js @@ -0,0 +1,50 @@ + +let utils = require('../../utils/utils.js'); +let fs = require('../../core/fs.js'); +let _ = require('underscore'); + +class Storage { + constructor(embark, options){ + this._storageConfig = options.storageConfig; + + let storageProviderCls = require(`../${this._storageConfig.upload.provider}/index.js`); + this._uploadProvider = new storageProviderCls(embark, this._storageConfig); /*eslint no-new: "off"*/ + + if(typeof this._uploadProvider.initProvider == 'function') this._uploadProvider.initProvider(); + if(typeof this._uploadProvider.commandlineDeploy == 'function') this._uploadProvider.commandlineDeploy(); + if(typeof this._uploadProvider.setServiceCheck == 'function') this._uploadProvider.setServiceCheck(); + if(typeof this._uploadProvider.addObjectToConsole == 'function') this._uploadProvider.addObjectToConsole(); + + // loop through all available providers and add "register provider" code in EmbarkJS + // which allows the provider to be set in the DApp + this._storageConfig.available_providers.forEach(providerStr => { + let storageProviderCls = require(`../${providerStr}/index.js`); + this._storageProvider = new storageProviderCls(this.storageConfig); /*eslint no-new: "off"*/ + if(typeof this._storageProvider.addProviderToEmbarkJS == 'function') this._storageProvider.addProviderToEmbarkJS(); + }); + + // add the code to call setProviders in embarkjs + this.addSetProviders(); + } + + addSetProviders() { + // TODO: make this a shouldAdd condition + if (this._storageConfig === {} || !this._storageConfig.dappConnection || !this._storageConfig.dappConnection.length) { + return; + } + + // filter list of dapp connections based on available_providers set in config + let hasSwarm = _.contains(this._storageConfig.available_providers, 'swarm'); // don't need to eval this in every loop iteration + let connectionsToSet = _.filter(this._storageConfig.dappConnection, (conn) => { + return _.contains(this._storageConfig.available_providers, conn.provider) || (conn === '$BZZ' && hasSwarm); + }); + + let code = ""; + code += "\n" + fs.readFileSync(utils.joinPath(__dirname, 'embarkjs.js')).toString(); + code += `\n__embarkStorage.setProviders(${JSON.stringify(connectionsToSet)}));`; + + this.embark.addCodeToEmbarkJS(code); + } +} + +module.exports = Storage; diff --git a/lib/modules/swarm/embarkjs.js b/lib/modules/swarm/embarkjs.js index 78d163ec..18ed0b08 100644 --- a/lib/modules/swarm/embarkjs.js +++ b/lib/modules/swarm/embarkjs.js @@ -1,18 +1,18 @@ -/*global web3 */ +/*global web3, some */ let __embarkSwarm = {}; const bytes = require("eth-lib/lib/bytes"); -import {some} from 'p-iteration'; __embarkSwarm.setProvider = function (options) { this.bzz = web3.bzz; this.protocol = options.protocol || 'http'; this.connectUrl = `${this.protocol}://${options.host}:${options.port}`; this.connectError = new Error(`Cannot connect to Swarm node on ${this.connectUrl}`); + this.useOnlyCurrentProvider = options.useOnlyCurrentProvider; //this._getUrl = options.getUrl || `${this.connectUrl}/bzzr:/`; return new Promise((resolve, reject) => { try { - if (!this.bzz.currentProvider) { + if (!this.bzz.currentProvider && !this.useOnlyCurrentProvider) { this.bzz.setProvider(this.connectUrl); } resolve(this); @@ -23,28 +23,6 @@ __embarkSwarm.setProvider = function (options) { }); }; -__embarkSwarm.setProviders = async function (dappConnOptions) { - var self = this; - try { - let workingConnFound = await some(dappConnOptions, async (dappConn) => { - if(dappConn === '$BZZ'){ - return self.isAvailable(); - } - else if(dappConn.provider === 'swarm') - { - // set the provider then check the connection, if true, use that provider, else, check next provider - await self.setProvider(dappConn); - return self.isAvailable(); - } - else return false; - }); - if(!workingConnFound) throw new Error('Could not connect to Swarm using any of the dappConnections in the storage config'); - else return self; - } catch (err) { - throw new Error('Failed to connect to Swarm: ' + err.message); - } -}; - __embarkSwarm.isAvailable = function () { return new Promise((resolve, reject) => { if (!this.bzz) { diff --git a/lib/modules/swarm/index.js b/lib/modules/swarm/index.js index f336c0f7..b14b2939 100644 --- a/lib/modules/swarm/index.js +++ b/lib/modules/swarm/index.js @@ -16,14 +16,14 @@ class Swarm { this.embark = embark; this.bzz = options.bzz; - this.initSwarmProvider(); - this.commandlineDeploy(); - this.setServiceCheck(); - this.addSwarmToEmbarkJS(); - this.addSetProvider(); + // this.initProvider(); + // this.commandlineDeploy(); + // this.setServiceCheck(); + // this.addProviderToEmbarkJS(); + // this.addSetProvider(); } - initSwarmProvider(){ + initProvider(){ if(!this.bzz.currentProvider) { this.bzz.setProvider(`${this.protocol}://${this.host}:${this.port}`); } @@ -76,7 +76,7 @@ class Swarm { }); } - addSwarmToEmbarkJS() { + addProviderToEmbarkJS() { let self = this; // TODO: make this a shouldAdd condition if (this.storageConfig === {}) { @@ -103,15 +103,15 @@ class Swarm { this.embark.addCodeToEmbarkJS(code); } - addSetProvider() { - let code = "\nEmbarkJS.Storage.setProviders('swarm'," + JSON.stringify(this.storageConfig.dappConnection) + ");"; + // addSetProvider() { + // let code = "\nEmbarkJS.Storage.setProviders('swarm'," + JSON.stringify(this.storageConfig.dappConnection) + ");"; - let shouldInit = (storageConfig) => { - return (this.storageConfig.dappConnection !== undefined && this.storageConfig.dappConnection.some((dappConn) => dappConn.provider === 'swarm') && storageConfig.enabled === true); - }; + // let shouldInit = (storageConfig) => { + // return (this.storageConfig.dappConnection !== undefined && this.storageConfig.dappConnection.some((dappConn) => dappConn.provider === 'swarm') && storageConfig.enabled === true); + // }; - this.embark.addProviderInit('storage', code, shouldInit); - } + // this.embark.addProviderInit('storage', code, shouldInit); + // } } module.exports = Swarm; From c0bdff1ae4c5fcd27055b310f4cbcd196eaba445 Mon Sep 17 00:00:00 2001 From: emizzle Date: Wed, 30 May 2018 16:34:36 +1000 Subject: [PATCH 3/3] Dynamic selection of storage provider now working based on improved storage config. swarm plugin now re-initialises the bzz object when it's availability is checked. this creates a much more stable swarm implementation on the dapp side. surrounded the storage provider init code block with embark env ready added alternate swarm gateway url can now upload dapp to ipfs and run swarm storage and vice versa --- js/embark.js | 12 ------ lib/cmd.js | 2 +- lib/core/engine.js | 1 - lib/i18n/locales/en.json | 6 ++- lib/index.js | 3 +- lib/modules/ipfs/embarkjs.js | 25 ++++++------ lib/modules/ipfs/index.js | 20 ++++----- lib/modules/storage/embarkjs.js | 20 +++++---- lib/modules/storage/index.js | 56 +++++++++++++++++++------- lib/modules/swarm/embarkjs.js | 52 +++++++++++++++--------- lib/modules/swarm/index.js | 45 ++++++++------------- lib/modules/swarm/upload.js | 5 ++- test_apps/test_app/config/storage.json | 6 +-- 13 files changed, 136 insertions(+), 117 deletions(-) diff --git a/js/embark.js b/js/embark.js index 87b1e8cf..10658533 100644 --- a/js/embark.js +++ b/js/embark.js @@ -230,18 +230,6 @@ EmbarkJS.Storage.setProvider = function(provider, options) { return providerObj.setProvider(options); }; -EmbarkJS.Storage.setProviders = function(provider, dappConnOptions) { - let providerObj = this.Providers[provider]; - - if (!providerObj) { - throw new Error('Unknown storage provider'); - } - - this.currentStorage = providerObj; - - return providerObj.setProviders(dappConnOptions); -}; - EmbarkJS.Storage.isAvailable = function(){ if (!this.currentStorage) { throw new Error('Storage provider not set; e.g EmbarkJS.Storage.setProvider("ipfs")'); diff --git a/lib/cmd.js b/lib/cmd.js index 2550e48f..0e5c5199 100644 --- a/lib/cmd.js +++ b/lib/cmd.js @@ -203,7 +203,7 @@ class Cmd { .option('--loglevel [loglevel]', __('level of logging to display') + ' ["error", "warn", "info", "debug", "trace"]', /^(error|warn|info|debug|trace)$/i, 'debug') .option('--locale [locale]', __('language to use (default: en)')) .option('-c, --client [client]', __('Use a specific ethereum client or simulator (supported: %s)', 'geth, testrpc')) - .description(__('Upload your dapp to a decentralized storage') + ' (e.g embark upload ipfs).') + .description(__('Upload your dapp to a decentralized storage') + '.') .action(function (env, _options) { i18n.setOrDetectLocale(_options.locale); _options.env = env || 'development'; diff --git a/lib/core/engine.js b/lib/core/engine.js index d45aa5bf..09a97d6c 100644 --- a/lib/core/engine.js +++ b/lib/core/engine.js @@ -133,7 +133,6 @@ class Engine { plugins: this.plugins }); this.events.on('code-generator-ready', function () { - console.log('CODE GENERATOR READY EVENT FIRED'); self.events.request('code', function (abi, contractsJSON) { pipeline.build(abi, contractsJSON, null, () => { if (self.watch) { diff --git a/lib/i18n/locales/en.json b/lib/i18n/locales/en.json index 33b0e605..a5bc133f 100644 --- a/lib/i18n/locales/en.json +++ b/lib/i18n/locales/en.json @@ -132,5 +132,9 @@ "IPFS node detected": "IPFS node detected", "Webserver is offline": "Webserver is offline", "DApp path length is too long: \"": "DApp path length is too long: \"", - "This is known to cause issues with some applications, please consider reducing your DApp path's length to 66 characters or less.": "This is known to cause issues with some applications, please consider reducing your DApp path's length to 66 characters or less." + "This is known to cause issues with some applications, please consider reducing your DApp path's length to 66 characters or less.": "This is known to cause issues with some applications, please consider reducing your DApp path's length to 66 characters or less.", + "deploying to swarm!": "deploying to swarm!", + "adding %s to swarm": "adding %s to swarm", + "successfully uploaded to swarm": "successfully uploaded to swarm", + "Cannot upload: {{platform}} node is not running on {{protocol}}://{{host}}{{port}}.": "Cannot upload: {{platform}} node is not running on {{protocol}}://{{host}}{{port}}." } \ No newline at end of file diff --git a/lib/index.js b/lib/index.js index 354947b6..06f3e1a6 100644 --- a/lib/index.js +++ b/lib/index.js @@ -332,7 +332,8 @@ class Embark { checkFn.fn(function (serviceCheckResult) { if (!serviceCheckResult.status || serviceCheckResult.status === 'off') { let config = engine.config.storageConfig.upload; - return callback({message: __('Cannot upload: {{platform}} node is not running on {{protocol}}://{{host}}:{{port}}.', {platform: platform, protocol: config.protocol, host: config.host, port: config.port})}); + let port = config.port ? ':' + config.port : ''; + return callback({message: __('Cannot upload: {{platform}} node is not running on {{protocol}}://{{host}}{{port}}.', {platform: platform, protocol: config.protocol, host: config.host, port: port})}); } callback(); }); diff --git a/lib/modules/ipfs/embarkjs.js b/lib/modules/ipfs/embarkjs.js index 39c849ed..82bf1932 100644 --- a/lib/modules/ipfs/embarkjs.js +++ b/lib/modules/ipfs/embarkjs.js @@ -7,7 +7,8 @@ __embarkIPFS.setProvider = function (options) { var promise = new Promise(function (resolve, reject) { try { if (options === undefined) { - self.ipfsConnection = IpfsApi('localhost', '5001'); + self._config = options; + self._ipfsConnection = IpfsApi('localhost', '5001'); self._getUrl = "http://localhost:8080/ipfs/"; } else { var ipfsOptions = {host: options.host || options.server, protocol: 'http'}; @@ -17,13 +18,13 @@ __embarkIPFS.setProvider = function (options) { if (options.port && options.port !== 'false') { ipfsOptions.port = options.port; } - self.ipfsConnection = IpfsApi(ipfsOptions); + self._ipfsConnection = IpfsApi(ipfsOptions); self._getUrl = options.getUrl || "http://localhost:8080/ipfs/"; } resolve(self); } catch (err) { console.error(err); - self.ipfsConnection = null; + self._ipfsConnection = null; reject(new Error('Failed to connect to IPFS')); } }); @@ -33,11 +34,11 @@ __embarkIPFS.setProvider = function (options) { __embarkIPFS.saveText = function (text) { const self = this; var promise = new Promise(function (resolve, reject) { - if (!self.ipfsConnection) { + if (!self._ipfsConnection) { var connectionError = new Error('No IPFS connection. Please ensure to call Embark.Storage.setProvider()'); reject(connectionError); } - self.ipfsConnection.add(self.ipfsConnection.Buffer.from(text), function (err, result) { + self._ipfsConnection.add(self._ipfsConnection.Buffer.from(text), function (err, result) { if (err) { reject(err); } else { @@ -54,11 +55,11 @@ __embarkIPFS.get = function (hash) { // TODO: detect type, then convert if needed //var ipfsHash = web3.toAscii(hash); var promise = new Promise(function (resolve, reject) { - if (!self.ipfsConnection) { + if (!self._ipfsConnection) { var connectionError = new Error('No IPFS connection. Please ensure to call Embark.Storage.setProvider()'); reject(connectionError); } - self.ipfsConnection.get(hash, function (err, files) { + self._ipfsConnection.get(hash, function (err, files) { if (err) { return reject(err); } @@ -78,15 +79,15 @@ __embarkIPFS.uploadFile = function (inputSelector) { } var promise = new Promise(function (resolve, reject) { - if (!self.ipfsConnection) { + if (!self._ipfsConnection) { var connectionError = new Error('No IPFS connection. Please ensure to call Embark.Storage.setProvider()'); reject(connectionError); } var reader = new FileReader(); reader.onloadend = function () { var fileContent = reader.result; - var buffer = self.ipfsConnection.Buffer.from(fileContent); - self.ipfsConnection.add(buffer, function (err, result) { + var buffer = self._ipfsConnection.Buffer.from(fileContent); + self._ipfsConnection.add(buffer, function (err, result) { if (err) { reject(err); } else { @@ -102,10 +103,10 @@ __embarkIPFS.uploadFile = function (inputSelector) { __embarkIPFS.isAvailable = function () { return new Promise((resolve) => { - if (!this.ipfsConnection) { + if (!this._ipfsConnection) { return resolve(false); } - this.ipfsConnection.id() + this._ipfsConnection.id() .then((id) => { resolve(Boolean(id)); }) diff --git a/lib/modules/ipfs/index.js b/lib/modules/ipfs/index.js index 19017ec8..78bf04e0 100644 --- a/lib/modules/ipfs/index.js +++ b/lib/modules/ipfs/index.js @@ -1,8 +1,8 @@ -let UploadIPFS = require('./upload.js'); -let utils = require('../../utils/utils.js'); -let fs = require('../../core/fs.js'); -let RunCode = require('../../coderunner/runCode'); -let IpfsApi = require('ipfs-api'); +const UploadIPFS = require('./upload.js'); +const utils = require('../../utils/utils.js'); +const fs = require('../../core/fs.js'); +const RunCode = require('../../coderunner/runCode'); +const IpfsApi = require('ipfs-api'); const _ = require('underscore'); class IPFS { @@ -17,18 +17,12 @@ class IPFS { this.protocol = options.protocol || this.storageConfig.upload.protocol; this.addCheck = options.addCheck; this.embark = embark; - - // this.commandlineDeploy(); - // this.setServiceCheck(); - // this.addProviderToEmbarkJS(); - // this.addSetProvider(); - // this.addObjectToConsole(); } commandlineDeploy() { let upload_ipfs = new UploadIPFS({ buildDir: this.buildDir || 'dist/', - storageConfig: this.storageConfig, + storageConfig: this.storageConfig.upload, configIpfsBin: this.storageConfig.ipfs_bin || "ipfs" }); @@ -127,7 +121,7 @@ class IPFS { // } addObjectToConsole() { - let ipfs = IpfsApi(this.storageConfig.host, this.storageConfig.port); + let ipfs = IpfsApi(this.host, this.port); RunCode.doEval("", {ipfs: ipfs}); } diff --git a/lib/modules/storage/embarkjs.js b/lib/modules/storage/embarkjs.js index dac27481..8f7c4151 100644 --- a/lib/modules/storage/embarkjs.js +++ b/lib/modules/storage/embarkjs.js @@ -5,20 +5,25 @@ import {findSeries} from 'p-iteration'; let __embarkStorage = {}; __embarkStorage.setProviders = async function (dappConnOptions) { - var self = this; try { let workingConnFound = await findSeries(dappConnOptions, async (dappConn) => { if(dappConn === '$BZZ' || dappConn.provider === 'swarm'){ - let options = dappConnOptions; - options.useOnlyCurrentProvider = dappConn === '$BZZ'; - await EmbarkJS.Storage.setProvider('swarm', options); - return EmbarkJS.Storage.isAvailable(); + let options = dappConn; + if(dappConn === '$BZZ') options = {"useOnlyGivenProvider": true}; + try{ + await EmbarkJS.Storage.setProvider('swarm', options); + let isAvailable = await EmbarkJS.Storage.isAvailable(); + return isAvailable; + }catch(err){ + return false; // catch errors for when bzz object not initialised but config has requested it to be used + } } else if(dappConn.provider === 'ipfs') { // set the provider then check the connection, if true, use that provider, else, check next provider try{ await EmbarkJS.Storage.setProvider('ipfs', dappConn); - return EmbarkJS.Storage.isAvailable(); + let isAvailable = await EmbarkJS.Storage.isAvailable(); + return isAvailable; } catch(err) { return false; } @@ -26,7 +31,6 @@ __embarkStorage.setProviders = async function (dappConnOptions) { }); if(!workingConnFound) throw new Error('Could not connect to a storage provider using any of the dappConnections in the storage config'); } catch (err) { - self.ipfsConnection = null; - throw new Error('Failed to connect to IPFS: ' + err.message); + throw new Error('Failed to connect to a storage provider: ' + err.message); } }; diff --git a/lib/modules/storage/index.js b/lib/modules/storage/index.js index af67d58c..1c878cb3 100644 --- a/lib/modules/storage/index.js +++ b/lib/modules/storage/index.js @@ -5,28 +5,52 @@ let _ = require('underscore'); class Storage { constructor(embark, options){ + this._embark = embark; this._storageConfig = options.storageConfig; let storageProviderCls = require(`../${this._storageConfig.upload.provider}/index.js`); - this._uploadProvider = new storageProviderCls(embark, this._storageConfig); /*eslint no-new: "off"*/ + let uploadProvider = new storageProviderCls(embark, options); /*eslint no-new: "off"*/ - if(typeof this._uploadProvider.initProvider == 'function') this._uploadProvider.initProvider(); - if(typeof this._uploadProvider.commandlineDeploy == 'function') this._uploadProvider.commandlineDeploy(); - if(typeof this._uploadProvider.setServiceCheck == 'function') this._uploadProvider.setServiceCheck(); - if(typeof this._uploadProvider.addObjectToConsole == 'function') this._uploadProvider.addObjectToConsole(); + if(typeof uploadProvider.commandlineDeploy == 'function') uploadProvider.commandlineDeploy(); + if(typeof uploadProvider.setServiceCheck == 'function') uploadProvider.setServiceCheck(); + if(typeof uploadProvider.addObjectToConsole == 'function') uploadProvider.addObjectToConsole(); - // loop through all available providers and add "register provider" code in EmbarkJS - // which allows the provider to be set in the DApp + // loop through all available providers and add the provider code to embarkjs this._storageConfig.available_providers.forEach(providerStr => { - let storageProviderCls = require(`../${providerStr}/index.js`); - this._storageProvider = new storageProviderCls(this.storageConfig); /*eslint no-new: "off"*/ - if(typeof this._storageProvider.addProviderToEmbarkJS == 'function') this._storageProvider.addProviderToEmbarkJS(); + let storageProvider; + + // check if we've already instantiated our storage class and reuse + if(providerStr === this._storageConfig.upload.provider){ + storageProvider = uploadProvider; + } + // otherwise instantiate the storage provider + else { + let storageProviderCls = require(`../${providerStr}/index.js`); + storageProvider = new storageProviderCls(embark, options); /*eslint no-new: "off"*/ + } + + // add __embarkSwarm and __embarkIPFS objects to EmbarkJS + if(typeof storageProvider.addProviderToEmbarkJS == 'function') storageProvider.addProviderToEmbarkJS(); }); - // add the code to call setProviders in embarkjs + // add the storage provider code (__embarkStorage) to embarkjs + this.addProviderToEmbarkJS(); + + // add the code to call setProviders in embarkjs after embark is ready this.addSetProviders(); } + addProviderToEmbarkJS(){ + // TODO: make this a shouldAdd condition + if (this._storageConfig === {} || !this._storageConfig.dappConnection || !this._storageConfig.dappConnection.length) { + return; + } + + let code = "\n" + fs.readFileSync(utils.joinPath(__dirname, 'embarkjs.js')).toString(); + + this._embark.addCodeToEmbarkJS(code); + } + addSetProviders() { // TODO: make this a shouldAdd condition if (this._storageConfig === {} || !this._storageConfig.dappConnection || !this._storageConfig.dappConnection.length) { @@ -39,11 +63,13 @@ class Storage { return _.contains(this._storageConfig.available_providers, conn.provider) || (conn === '$BZZ' && hasSwarm); }); - let code = ""; - code += "\n" + fs.readFileSync(utils.joinPath(__dirname, 'embarkjs.js')).toString(); - code += `\n__embarkStorage.setProviders(${JSON.stringify(connectionsToSet)}));`; + let code = `\n__embarkStorage.setProviders(${JSON.stringify(connectionsToSet)});`; + + let shouldInit = (storageConfig) => { + return (connectionsToSet !== undefined && connectionsToSet.length > 0 && storageConfig.enabled === true); + }; - this.embark.addCodeToEmbarkJS(code); + this._embark.addProviderInit('storage', code, shouldInit); } } diff --git a/lib/modules/swarm/embarkjs.js b/lib/modules/swarm/embarkjs.js index 18ed0b08..7f5dc4a5 100644 --- a/lib/modules/swarm/embarkjs.js +++ b/lib/modules/swarm/embarkjs.js @@ -1,37 +1,49 @@ -/*global web3, some */ +/*global web3 */ let __embarkSwarm = {}; const bytes = require("eth-lib/lib/bytes"); __embarkSwarm.setProvider = function (options) { - this.bzz = web3.bzz; - this.protocol = options.protocol || 'http'; - this.connectUrl = `${this.protocol}://${options.host}:${options.port}`; - this.connectError = new Error(`Cannot connect to Swarm node on ${this.connectUrl}`); - this.useOnlyCurrentProvider = options.useOnlyCurrentProvider; - //this._getUrl = options.getUrl || `${this.connectUrl}/bzzr:/`; + let protocol = options.protocol || 'http'; + let port = options.port ? `:${options.port}` : ''; + + this._config = options; + this._connectUrl = `${protocol}://${options.host}${port}`; + this._connectError = new Error(`Cannot connect to Swarm node on ${this._connectUrl}`); return new Promise((resolve, reject) => { try { - if (!this.bzz.currentProvider && !this.useOnlyCurrentProvider) { - this.bzz.setProvider(this.connectUrl); + if (!web3.bzz.currentProvider && !options.useOnlyGivenProvider) { + web3.bzz.setProvider(this._connectUrl); + } + else if(options.useOnlyGivenProvider && web3.bzz.givenProvider !== null){ + web3.bzz.setProvider(web3.bzz.givenProvider); } resolve(this); } catch (err) { console.log(err); - reject(this.connectError); + reject(this._connectError); } }); }; __embarkSwarm.isAvailable = function () { return new Promise((resolve, reject) => { - if (!this.bzz) { + // if web3 swarm object doesn't exist + if (!web3.bzz) { return resolve(false); } - this.bzz.isAvailable() + // swarm obj exists, but has no provider set (seems to happen a LOT!), + // try setting the provider to our currently set provider again + else if(!web3.bzz.currentProvider && this._config.host){ + web3.bzz.setProvider(this._connectUrl); + } + if (!web3.bzz.currentProvider) { + return resolve(false); + } + web3.bzz.isAvailable() .then(resolve) .catch(() => { - reject(this.connectError); + reject(this._connectError); }); }); }; @@ -40,9 +52,9 @@ __embarkSwarm.saveText = function (text) { return new Promise((resolve, reject) => { this.isAvailable().then((isAvailable) => { if (!isAvailable) { - return reject(this.connectError); + return reject(this._connectError); } - this.bzz.upload(text) + web3.bzz.upload(text) .then(resolve) .catch(reject); }).catch(reject); @@ -53,9 +65,9 @@ __embarkSwarm.get = function (hash) { return new Promise((resolve, reject) => { this.isAvailable().then((isAvailable) => { if (!isAvailable) { - return reject(this.connectError); + return reject(this._connectError); } - this.bzz.download(hash) + web3.bzz.download(hash) .then((uint8Array) => resolve(bytes.toString(bytes.fromUint8Array(uint8Array)))) .catch(reject); }).catch(reject); @@ -75,9 +87,9 @@ __embarkSwarm.uploadFile = function (inputSelector) { const fileContent = new Uint8Array(event.target.result); this.isAvailable().then((isAvailable) => { if (!isAvailable) { - return reject(this.connectError); + return reject(this._connectError); } - this.bzz.upload(fileContent) + web3.bzz.upload(fileContent) .then(resolve) .catch(reject); }).catch(reject); @@ -88,6 +100,6 @@ __embarkSwarm.uploadFile = function (inputSelector) { }; __embarkSwarm.getUrl = function (hash) { - return this._getUrl + hash; + return `${this._config.getUrl || this._connectUrl + '/bzzr:/'}${hash}`; }; diff --git a/lib/modules/swarm/index.js b/lib/modules/swarm/index.js index b14b2939..b40d61c8 100644 --- a/lib/modules/swarm/index.js +++ b/lib/modules/swarm/index.js @@ -1,6 +1,7 @@ -let UploadSwarm = require('./upload.js'); -let utils = require('../../utils/utils.js'); -let fs = require('../../core/fs.js'); +const UploadSwarm = require('./upload.js'); +const utils = require('../../utils/utils.js'); +const fs = require('../../core/fs.js'); +const Web3Bzz = require('web3-bzz'); class Swarm { @@ -9,30 +10,26 @@ class Swarm { this.events = embark.events; this.buildDir = options.buildDir; this.storageConfig = options.storageConfig; - this.host = options.host || options.storageConfig.upload.host; - this.port = options.port || options.storageConfig.upload.port; - this.protocol = options.protocol || options.storageConfig.upload.protocol; this.addCheck = options.addCheck; this.embark = embark; - this.bzz = options.bzz; - - // this.initProvider(); - // this.commandlineDeploy(); - // this.setServiceCheck(); - // this.addProviderToEmbarkJS(); - // this.addSetProvider(); - } - - initProvider(){ - if(!this.bzz.currentProvider) { - this.bzz.setProvider(`${this.protocol}://${this.host}:${this.port}`); - } + + let host = options.host || options.storageConfig.upload.host; + let port = options.port || options.storageConfig.upload.port; + if(port) port = ':' + port; + else port = ''; + let protocol = options.protocol || options.storageConfig.upload.protocol || 'http'; + this.providerUrl = `${protocol}://${host}${port}`; + this.getUrl = options.storageConfig.upload.getUrl || this.providerUrl + '/bzzr:/'; + + this.bzz = new Web3Bzz(this.providerUrl); } commandlineDeploy() { this.upload_swarm = new UploadSwarm({ buildDir: this.buildDir || 'dist/', storageConfig: this.storageConfig, + connectUrl: this.providerUrl, + getUrl: this.getUrl, bzz: this.bzz }); @@ -102,16 +99,6 @@ class Swarm { this.embark.addCodeToEmbarkJS(code); } - - // addSetProvider() { - // let code = "\nEmbarkJS.Storage.setProviders('swarm'," + JSON.stringify(this.storageConfig.dappConnection) + ");"; - - // let shouldInit = (storageConfig) => { - // return (this.storageConfig.dappConnection !== undefined && this.storageConfig.dappConnection.some((dappConn) => dappConn.provider === 'swarm') && storageConfig.enabled === true); - // }; - - // this.embark.addProviderInit('storage', code, shouldInit); - // } } module.exports = Swarm; diff --git a/lib/modules/swarm/upload.js b/lib/modules/swarm/upload.js index 4defd911..607e361d 100644 --- a/lib/modules/swarm/upload.js +++ b/lib/modules/swarm/upload.js @@ -7,6 +7,8 @@ class Swarm { this.buildDir = options.buildDir || 'dist/'; this.bzz = options.bzz; this.storageConfig = options.storageConfig; + this.providerUrl = options.providerUrl; + this.getUrl = options.getUrl; } deploy() { @@ -31,7 +33,8 @@ class Swarm { if (!dir_hash) { return callback('No directory hash was returned'); } - console.log(("=== " + __("DApp available at") + `${self.storageConfig.getUrl}${dir_hash}/`).green); + console.log(("=== " + __("DApp available at") + ` ${self.getUrl}${dir_hash}/`).green); + console.log(("=== " + __("DApp available at") + ` http://swarm-gateways.net/bzzr:/${dir_hash}`).green); callback(); } diff --git a/test_apps/test_app/config/storage.json b/test_apps/test_app/config/storage.json index 6d06e3c7..13912156 100644 --- a/test_apps/test_app/config/storage.json +++ b/test_apps/test_app/config/storage.json @@ -20,10 +20,10 @@ "development": { "enabled": true, "upload": { - "provider": "ipfs", + "provider": "swarm", "host": "localhost", - "port": 5001, - "getUrl": "http://localhost:8080/ipfs/" + "port": 8500, + "getUrl": "http://localhost:8500/bzzr:/" } }, "livenet": {