Merge branch 'develop' of https://github.com/embark-framework/embark into develop

This commit is contained in:
Santiago Gonzalez Toral 2018-09-13 16:59:58 -05:00
commit 502befc453
37 changed files with 2672 additions and 2646 deletions

View File

@ -7,3 +7,5 @@
### Review ### Review
<use @mentions for quick questions, specific feedback, and progress updates.> <use @mentions for quick questions, specific feedback, and progress updates.>
### Cool Spaceship Picture

View File

@ -10,26 +10,6 @@ try {
} }
} }
function launchEmbark() { var Cmd = require('../cmd/cmd');
var Cmd = require('../cmd/cmd'); var cli = new Cmd();
var cli = new Cmd(); cli.process(process.argv);
cli.process(process.argv);
}
const path = require('path');
try {
const dappPackage = require(path.join(process.cwd(), 'package.json'));
require(path.join(process.cwd(), 'embark.json')); // Make sure we are in a Dapp
require('check-dependencies')(dappPackage, (state) => {
if (state.status) {
require('colors');
console.error('\nMissing dependencies. Please run npm install'.red);
process.exit();
}
launchEmbark();
});
} catch (_e) {
// We are not in a Dapp
launchEmbark();
}

View File

@ -30,6 +30,25 @@ process.env.NODE_PATH = utils.joinPath(process.env.EMBARK_PATH, 'node_modules')
(process.env.NODE_PATH ? require('path').delimiter : '') + (process.env.NODE_PATH ? require('path').delimiter : '') +
(process.env.NODE_PATH || ''); (process.env.NODE_PATH || '');
function checkDeps() {
const path = require('path');
try {
const dappPackage = require(path.join(process.cwd(), 'package.json'));
require(path.join(process.cwd(), 'embark.json')); // Make sure we are in a Dapp
require('check-dependencies')(dappPackage, (state) => {
if (state.status) {
require('colors');
console.error('\nMissing dependencies. Please run npm install'.red);
process.exit();
}
return true;
});
} catch (_e) {
// We are not in a Dapp
return true;
}
}
class Cmd { class Cmd {
constructor() { constructor() {
program.version(embark.version); program.version(embark.version);
@ -129,6 +148,7 @@ class Cmd {
.option('--pipeline [pipeline]', __('webpack config to use (default: production)')) .option('--pipeline [pipeline]', __('webpack config to use (default: production)'))
.description(__('deploy and build dapp at ') + 'dist/ (default: development)') .description(__('deploy and build dapp at ') + 'dist/ (default: development)')
.action(function(env, _options) { .action(function(env, _options) {
checkDeps();
i18n.setOrDetectLocale(_options.locale); i18n.setOrDetectLocale(_options.locale);
_options.env = env || 'development'; _options.env = env || 'development';
_options.logFile = _options.logfile; // fix casing _options.logFile = _options.logfile; // fix casing
@ -155,6 +175,7 @@ class Cmd {
.option('--pipeline [pipeline]', __('webpack config to use (default: development)')) .option('--pipeline [pipeline]', __('webpack config to use (default: development)'))
.description(__('run dapp (default: %s)', 'development')) .description(__('run dapp (default: %s)', 'development'))
.action(function(env, options) { .action(function(env, options) {
checkDeps();
i18n.setOrDetectLocale(options.locale); i18n.setOrDetectLocale(options.locale);
embark.run({ embark.run({
env: env || 'development', env: env || 'development',
@ -181,6 +202,7 @@ class Cmd {
.option('--pipeline [pipeline]', __('webpack config to use (default: development)')) .option('--pipeline [pipeline]', __('webpack config to use (default: development)'))
.description(__('Start the Embark console')) .description(__('Start the Embark console'))
.action(function(env, options) { .action(function(env, options) {
checkDeps();
i18n.setOrDetectLocale(options.locale); i18n.setOrDetectLocale(options.locale);
embark.console({ embark.console({
env: env || 'development', env: env || 'development',
@ -200,6 +222,7 @@ class Cmd {
.option('--locale [locale]', __('language to use (default: en)')) .option('--locale [locale]', __('language to use (default: en)'))
.description(__('run blockchain server (default: %s)', 'development')) .description(__('run blockchain server (default: %s)', 'development'))
.action(function(env, options) { .action(function(env, options) {
checkDeps();
i18n.setOrDetectLocale(options.locale); i18n.setOrDetectLocale(options.locale);
embark.initConfig(env || 'development', { embark.initConfig(env || 'development', {
embarkConfig: 'embark.json', embarkConfig: 'embark.json',
@ -222,6 +245,7 @@ class Cmd {
.option('--locale [locale]', __('language to use (default: en)')) .option('--locale [locale]', __('language to use (default: en)'))
.action(function(env, options) { .action(function(env, options) {
checkDeps();
i18n.setOrDetectLocale(options.locale); i18n.setOrDetectLocale(options.locale);
embark.initConfig(env || 'development', { embark.initConfig(env || 'development', {
embarkConfig: 'embark.json', embarkConfig: 'embark.json',
@ -246,6 +270,7 @@ class Cmd {
.option('--loglevel [loglevel]', __('level of logging to display') + ' ["error", "warn", "info", "debug", "trace"]', /^(error|warn|info|debug|trace)$/i, 'warn') .option('--loglevel [loglevel]', __('level of logging to display') + ' ["error", "warn", "info", "debug", "trace"]', /^(error|warn|info|debug|trace)$/i, 'warn')
.description(__('run tests')) .description(__('run tests'))
.action(function(file, options) { .action(function(file, options) {
checkDeps();
i18n.setOrDetectLocale(options.locale); i18n.setOrDetectLocale(options.locale);
embark.runTests({file, loglevel: options.loglevel, gasDetails: options.gasDetails, node: options.node}); embark.runTests({file, loglevel: options.loglevel, gasDetails: options.gasDetails, node: options.node});
}); });
@ -262,6 +287,7 @@ class Cmd {
.option('--pipeline [pipeline]', __('webpack config to use (default: production)')) .option('--pipeline [pipeline]', __('webpack config to use (default: production)'))
.description(__('Upload your dapp to a decentralized storage') + '.') .description(__('Upload your dapp to a decentralized storage') + '.')
.action(function(env, _options) { .action(function(env, _options) {
checkDeps();
i18n.setOrDetectLocale(_options.locale); i18n.setOrDetectLocale(_options.locale);
if (env === "ipfs" || env === "swarm") { if (env === "ipfs" || env === "swarm") {
console.warn(("did you mean " + "embark upload".bold + " ?").underline); console.warn(("did you mean " + "embark upload".bold + " ?").underline);
@ -286,6 +312,7 @@ class Cmd {
.option('--locale [locale]', __('language to use (default: en)')) .option('--locale [locale]', __('language to use (default: en)'))
.description(__('generates documentation based on the smart contracts configured')) .description(__('generates documentation based on the smart contracts configured'))
.action(function(env, options) { .action(function(env, options) {
checkDeps();
i18n.setOrDetectLocale(options.locale); i18n.setOrDetectLocale(options.locale);
embark.graph({ embark.graph({
env: env || 'development', env: env || 'development',
@ -316,6 +343,10 @@ class Cmd {
.command('eject-webpack') .command('eject-webpack')
.description(__('copy the default webpack config into your dapp for customization')) .description(__('copy the default webpack config into your dapp for customization'))
.action(function() { .action(function() {
embark.initConfig('development', {
embarkConfig: 'embark.json',
interceptLogs: false
});
embark.ejectWebpack(); embark.ejectWebpack();
}); });
} }

View File

@ -79,7 +79,8 @@ class EmbarkController {
context: self.context, context: self.context,
useDashboard: options.useDashboard, useDashboard: options.useDashboard,
webServerConfig: webServerConfig, webServerConfig: webServerConfig,
webpackConfigName: options.webpackConfigName webpackConfigName: options.webpackConfigName,
ipcRole: 'server'
}); });
async.waterfall([ async.waterfall([
@ -127,6 +128,7 @@ class EmbarkController {
engine.startService("codeGenerator"); engine.startService("codeGenerator");
engine.startService("namingSystem"); engine.startService("namingSystem");
engine.startService("console"); engine.startService("console");
engine.startService("pluginCommand");
engine.events.on('check:backOnline:Ethereum', function () { engine.events.on('check:backOnline:Ethereum', function () {
engine.logger.info(__('Ethereum node detected') + '..'); engine.logger.info(__('Ethereum node detected') + '..');
@ -204,8 +206,10 @@ class EmbarkController {
engine.startService("pipeline"); engine.startService("pipeline");
} }
engine.startService("deployment", {onlyCompile: options.onlyCompile}); engine.startService("deployment", {onlyCompile: options.onlyCompile});
engine.startService("storage"); if (!options.onlyCompile) {
engine.startService("codeGenerator"); engine.startService("storage");
engine.startService("codeGenerator");
}
callback(); callback();
}, },
@ -266,28 +270,24 @@ class EmbarkController {
engine.logger.info(__("loaded plugins") + ": " + pluginList.join(", ")); engine.logger.info(__("loaded plugins") + ": " + pluginList.join(", "));
} }
engine.ipc.connect((err) => { if (engine.ipc.connected) {
if (err) {
engine.startService("processManager");
engine.startService("serviceMonitor");
engine.startService("libraryManager");
engine.startService("codeRunner");
engine.startService("web3");
engine.startService("pipeline");
engine.startService("deployment");
engine.startService("storage");
engine.startService("codeGenerator");
engine.startService("webServer");
engine.startService("namingSystem");
engine.startService("console");
return callback();
}
engine.startService("codeRunner"); engine.startService("codeRunner");
engine.startService("console"); engine.startService("console");
callback(); return callback();
}); }
engine.startService("processManager");
engine.startService("serviceMonitor");
engine.startService("libraryManager");
engine.startService("codeRunner");
engine.startService("web3");
engine.startService("pipeline");
engine.startService("deployment");
engine.startService("storage");
engine.startService("codeGenerator");
engine.startService("namingSystem");
engine.startService("console");
engine.startService("pluginCommand");
callback();
}, },
function web3IPC(callback) { function web3IPC(callback) {
// Do specific work in case we are connected to a socket: // Do specific work in case we are connected to a socket:

View File

@ -282,9 +282,7 @@ Config.prototype.loadNameSystemConfigFile = function() {
// todo: spec out names for registration in the file itself for a dev chain // todo: spec out names for registration in the file itself for a dev chain
var configObject = { var configObject = {
"default": { "default": {
"available_providers": ["ens"], "enabled": false
"provider": "ens",
"enabled": true
} }
}; };

View File

@ -18,6 +18,7 @@ class Engine {
this.useDashboard = options.useDashboard; this.useDashboard = options.useDashboard;
this.webServerConfig = options.webServerConfig; this.webServerConfig = options.webServerConfig;
this.webpackConfigName = options.webpackConfigName; this.webpackConfigName = options.webpackConfigName;
this.ipcRole = options.ipcRole || 'client';
} }
init(_options, callback) { init(_options, callback) {
@ -38,14 +39,16 @@ class Engine {
utils.interceptLogs(console, this.logger); utils.interceptLogs(console, this.logger);
} }
this.ipc = new IPC({logger: this.logger, ipcRole: 'client'}); this.ipc = new IPC({logger: this.logger, ipcRole: this.ipcRole});
this.ipc.connect((err) => { if (this.ipc.isClient()) {
if(err) { return this.ipc.connect((_err) => {
this.ipc = new IPC({logger: this.logger, ipcRole: 'server'}); callback();
this.ipc.serve(); });
} } else if (this.ipc.isServer()) {
callback(); this.ipc.serve();
}); return callback();
}
callback();
} }
registerModule(moduleName, options) { registerModule(moduleName, options) {
@ -70,7 +73,8 @@ class Engine {
"processManager": this.processManagerService, "processManager": this.processManagerService,
"storage": this.storageService, "storage": this.storageService,
"graph": this.graphService, "graph": this.graphService,
"codeCoverage": this.codeCoverageService "codeCoverage": this.codeCoverageService,
"pluginCommand": this.pluginCommandService
}; };
let service = services[serviceName]; let service = services[serviceName];
@ -130,6 +134,10 @@ class Engine {
this.servicesMonitor.startMonitor(); this.servicesMonitor.startMonitor();
} }
pluginCommandService() {
this.registerModule('plugin_cmd', {embarkConfigFile: this.embarkConfig, embarkConfig: this.config.embarkConfig, packageFile: 'package.json'});
}
namingSystem(_options) { namingSystem(_options) {
this.registerModule('ens'); this.registerModule('ens');
} }

View File

@ -24,10 +24,11 @@ class File {
// Only supported in Solidity // Only supported in Solidity
return callback(null, content); return callback(null, content);
} }
const regex = /import ["|']([-a-zA-Z0-9@:%_+.~#?&\/=]+)["|'];/g; const regex = /import ["']([-a-zA-Z0-9@:%_+.~#?&\/=]+)["'];/g;
let matches; let matches;
const filesToDownload = []; const filesToDownload = [];
const pathWithoutFile = path.dirname(self.path); const pathWithoutFile = path.dirname(self.path);
let newContent = content;
while ((matches = regex.exec(content))) { while ((matches = regex.exec(content))) {
const httpFileObj = utils.getExternalContractUrl(matches[1]); const httpFileObj = utils.getExternalContractUrl(matches[1]);
const fileObj = { const fileObj = {
@ -36,7 +37,7 @@ class File {
}; };
if (httpFileObj) { if (httpFileObj) {
// Replace http import by filePath import in content // Replace http import by filePath import in content
content = content.replace(matches[1], httpFileObj.filePath); newContent = newContent.replace(matches[1], httpFileObj.filePath);
fileObj.fileRelativePath = httpFileObj.filePath; fileObj.fileRelativePath = httpFileObj.filePath;
fileObj.url = httpFileObj.url; fileObj.url = httpFileObj.url;
@ -49,7 +50,7 @@ class File {
if (self.downloadedImports) { if (self.downloadedImports) {
// We already parsed this file // We already parsed this file
return callback(null, content); return callback(null, newContent);
} }
self.downloadedImports = true; self.downloadedImports = true;
async.each(filesToDownload, ((fileObj, eachCb) => { async.each(filesToDownload, ((fileObj, eachCb) => {
@ -57,7 +58,7 @@ class File {
eachCb(); eachCb();
}); });
}), (err) => { }), (err) => {
callback(err, content); callback(err, newContent);
}); });
} }

View File

@ -8,7 +8,7 @@ class CodeRunner {
this.events = options.events; this.events = options.events;
this.ipc = options.ipc; this.ipc = options.ipc;
this.commands = []; this.commands = [];
this.runCode = new RunCode(); this.runCode = new RunCode({logger: this.logger});
this.registerIpcEvents(); this.registerIpcEvents();
this.IpcClientListen(); this.IpcClientListen();
this.registerEvents(); this.registerEvents();

View File

@ -1,15 +1,19 @@
const vm = require('vm'); const vm = require('vm');
class RunCode { class RunCode {
constructor() { constructor({logger}) {
this.context = Object.assign({}, {global, console, exports, require, module, __filename, __dirname}); this.logger = logger;
this.context = Object.assign({}, {
global, console, exports, require, module, __filename, __dirname, process,
setTimeout, setInterval, clearTimeout, clearInterval
});
} }
doEval(code) { doEval(code) {
try { try {
return vm.runInNewContext(code, this.context); return vm.runInNewContext(code, this.context);
} catch(e) { } catch(e) {
console.error(e.message); this.logger.error(e.message);
} }
} }

View File

@ -29,6 +29,7 @@ class Console {
// TODO: only if the blockchain is actually active! // TODO: only if the blockchain is actually active!
// will need to pass te current embark state here // will need to pass te current embark state here
'ipfs - ' + __('instantiated js-ipfs object configured to the current environment (available if ipfs is enabled)'), 'ipfs - ' + __('instantiated js-ipfs object configured to the current environment (available if ipfs is enabled)'),
'swarm - ' + __('instantiated swarm-api object configured to the current environment (available if swarm is enabled)'),
'web3 - ' + __('instantiated web3.js object configured to the current environment'), 'web3 - ' + __('instantiated web3.js object configured to the current environment'),
'EmbarkJS - ' + __('EmbarkJS static functions for Storage, Messages, Names, etc.'), 'EmbarkJS - ' + __('EmbarkJS static functions for Storage, Messages, Names, etc.'),
'quit - ' + __('to immediatly exit (alias: exit)'), 'quit - ' + __('to immediatly exit (alias: exit)'),

View File

@ -117,7 +117,7 @@ class ContractsManager {
contract.code = compiledContract.code; contract.code = compiledContract.code;
contract.runtimeBytecode = compiledContract.runtimeBytecode; contract.runtimeBytecode = compiledContract.runtimeBytecode;
contract.realRuntimeBytecode = (contract.realRuntimeBytecode || contract.runtimeBytecode); contract.realRuntimeBytecode = (compiledContract.realRuntimeBytecode || compiledContract.runtimeBytecode);
contract.swarmHash = compiledContract.swarmHash; contract.swarmHash = compiledContract.swarmHash;
contract.gasEstimates = compiledContract.gasEstimates; contract.gasEstimates = compiledContract.gasEstimates;
contract.functionHashes = compiledContract.functionHashes; contract.functionHashes = compiledContract.functionHashes;

View File

@ -38,7 +38,7 @@ class ContractDeployer {
const match = arg.match(/\$accounts\[([0-9]+)]/); const match = arg.match(/\$accounts\[([0-9]+)]/);
if (match) { if (match) {
if (!accounts[match[1]]) { if (!accounts[match[1]]) {
return cb(__('No corresponding account at index $d', match[1])); return cb(__('No corresponding account at index %d', match[1]));
} }
return cb(null, accounts[match[1]]); return cb(null, accounts[match[1]]);
} }

View File

@ -52,6 +52,7 @@ class DeployManager {
self.events.emit("deploy:beforeAll"); self.events.emit("deploy:beforeAll");
const contractDeploys = {}; const contractDeploys = {};
const errors = [];
contracts.forEach(contract => { contracts.forEach(contract => {
function deploy(result, callback) { function deploy(result, callback) {
if (typeof result === 'function') { if (typeof result === 'function') {
@ -59,7 +60,12 @@ class DeployManager {
} }
contract._gasLimit = self.gasLimit; contract._gasLimit = self.gasLimit;
self.events.request('deploy:contract', contract, (err) => { self.events.request('deploy:contract', contract, (err) => {
callback(err); if (err) {
contract.error = err.message || err;
self.logger.error(err.message || err);
errors.push(err);
}
callback();
}); });
} }
@ -72,19 +78,24 @@ class DeployManager {
contractDeploys[className].push(deploy); contractDeploys[className].push(deploy);
}); });
async.auto(contractDeploys, function(err, _results) { try {
if (err) { async.auto(contractDeploys, function(_err, _results) {
self.logger.error(__("error deploying contracts")); if (errors.length) {
self.logger.error(err.message); _err = __("Error deploying contracts. Please fix errors to continue.");
self.logger.debug(err.stack); self.logger.error(_err);
} return done(_err);
if (contracts.length === 0) { }
self.logger.info(__("no contracts found")); if (contracts.length === 0) {
return done(); self.logger.info(__("no contracts found"));
} return done();
self.logger.info(__("finished deploying contracts")); }
done(err); self.logger.info(__("finished deploying contracts"));
}); done(err);
});
} catch (e) {
self.logger.error(e.message || e);
done(__('Error deploying'));
}
}); });
}); });
} }

View File

@ -36,7 +36,7 @@ function registerSubDomain(ens, registrar, resolver, defaultAccount, subdomain,
callback(null, transaction); callback(null, transaction);
}) })
.catch(err => { .catch(err => {
logger.error(err); logger.error(err.message || err);
callback('Failed to register with error: ' + (err.message || err)); callback('Failed to register with error: ' + (err.message || err));
}); });
} }

View File

@ -1,11 +1,11 @@
/*global IpfsApi*/ /*global IpfsApi*/
let __embarkIPFS = {}; const __embarkIPFS = {};
const NoConnectionError = 'No IPFS connection. Please ensure to call Embark.Storage.setProvider()'; const NoConnectionError = 'No IPFS connection. Please ensure to call Embark.Storage.setProvider()';
__embarkIPFS.setProvider = function (options) { __embarkIPFS.setProvider = function (options) {
var self = this; const self = this;
return new Promise(function (resolve, reject) { return new Promise(function (resolve, reject) {
try { try {
if (!options) { if (!options) {
@ -13,7 +13,7 @@ __embarkIPFS.setProvider = function (options) {
self._ipfsConnection = IpfsApi('localhost', '5001'); self._ipfsConnection = IpfsApi('localhost', '5001');
self._getUrl = "http://localhost:8080/ipfs/"; self._getUrl = "http://localhost:8080/ipfs/";
} else { } else {
var ipfsOptions = {host: options.host || options.server, protocol: 'http'}; const ipfsOptions = {host: options.host || options.server, protocol: 'http'};
if (options.protocol) { if (options.protocol) {
ipfsOptions.protocol = options.protocol; ipfsOptions.protocol = options.protocol;
} }
@ -50,10 +50,9 @@ __embarkIPFS.isAvailable = function () {
__embarkIPFS.saveText = function (text) { __embarkIPFS.saveText = function (text) {
const self = this; const self = this;
var promise = new Promise(function (resolve, reject) { return new Promise(function (resolve, reject) {
if (!self._ipfsConnection) { if (!self._ipfsConnection) {
var connectionError = new Error(NoConnectionError); return reject(new Error(NoConnectionError));
return 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) { if (err) {
@ -63,15 +62,13 @@ __embarkIPFS.saveText = function (text) {
resolve(result[0].path); resolve(result[0].path);
}); });
}); });
return promise;
}; };
__embarkIPFS.get = function (hash) { __embarkIPFS.get = function (hash) {
const self = this; const self = this;
// TODO: detect type, then convert if needed // TODO: detect type, then convert if needed
//var ipfsHash = web3.toAscii(hash); //var ipfsHash = web3.toAscii(hash);
var promise = new Promise(function (resolve, reject) { return new Promise(function (resolve, reject) {
if (!self._ipfsConnection) { if (!self._ipfsConnection) {
var connectionError = new Error(NoConnectionError); var connectionError = new Error(NoConnectionError);
return reject(connectionError); return reject(connectionError);
@ -83,27 +80,23 @@ __embarkIPFS.get = function (hash) {
resolve(files[0].content.toString()); resolve(files[0].content.toString());
}); });
}); });
return promise;
}; };
__embarkIPFS.uploadFile = function (inputSelector) { __embarkIPFS.uploadFile = function (inputSelector) {
const self = this; const self = this;
var file = inputSelector[0].files[0]; const file = inputSelector[0].files[0];
if (file === undefined) { if (file === undefined) {
throw new Error('no file found'); throw new Error('no file found');
} }
var promise = new Promise(function (resolve, reject) { return new Promise(function (resolve, reject) {
if (!self._ipfsConnection) { if (!self._ipfsConnection) {
var connectionError = new Error(NoConnectionError); return reject(new Error(NoConnectionError));
return reject(connectionError);
} }
var reader = new FileReader(); const reader = new FileReader();
reader.onloadend = function () { reader.onloadend = function () {
var fileContent = reader.result; const buffer = self._ipfsConnection.Buffer.from(reader.result);
var buffer = self._ipfsConnection.Buffer.from(fileContent);
self._ipfsConnection.add(buffer, function (err, result) { self._ipfsConnection.add(buffer, function (err, result) {
if (err) { if (err) {
return reject(err); return reject(err);
@ -114,8 +107,6 @@ __embarkIPFS.uploadFile = function (inputSelector) {
}; };
reader.readAsArrayBuffer(file); reader.readAsArrayBuffer(file);
}); });
return promise;
}; };
__embarkIPFS.getUrl = function (hash) { __embarkIPFS.getUrl = function (hash) {
@ -125,8 +116,7 @@ __embarkIPFS.getUrl = function (hash) {
__embarkIPFS.resolve = function (name, callback) { __embarkIPFS.resolve = function (name, callback) {
callback = callback || function () {}; callback = callback || function () {};
if (!this._ipfsConnection) { if (!this._ipfsConnection) {
var connectionError = new Error(NoConnectionError); return callback(new Error(NoConnectionError));
return callback(connectionError);
} }
this._ipfsConnection.name.resolve(name) this._ipfsConnection.name.resolve(name)
@ -144,6 +134,10 @@ __embarkIPFS.register = function(addr, callback) {
return new Error(NoConnectionError); return new Error(NoConnectionError);
} }
if (addr.length !== 46 || !addr.startsWith('Qm')) {
return callback('String is not an IPFS hash');
}
this._ipfsConnection.name.publish("/ipfs/" + addr) this._ipfsConnection.name.publish("/ipfs/" + addr)
.then(res => { .then(res => {
callback(null, res.Name); callback(null, res.Name);

View File

@ -0,0 +1,58 @@
let fs = require('./../../core/fs.js');
let utils = require('./../../utils/utils.js');
let async = require('async');
class PluginCommand {
constructor(embark) {
this.embark = embark;
this.config = this.embark.pluginConfig;
this.embarkConfig = this.config.embarkConfig;
this.registerCommand();
}
registerCommand() {
const self = this;
self.embark.registerConsoleCommand((cmd, _options) => {
let cmdArray = cmd.split(' ');
cmdArray = cmdArray.filter(cmd => cmd.trim().length > 0);
let cmdName = cmdArray[0];
return {
match: () => cmdName === 'plugin',
process: (callback) => {
if(cmdArray.length < 3 || cmdArray[1] !== 'install' || typeof cmdArray[2] === 'undefined') {
return callback('invalid use of plugin command. Please use plugin install <package>');
}
let npmInstall = ['npm', 'install', '--save'];
npmInstall = npmInstall.concat(cmdArray.slice(2));
let npmPackage = npmInstall[3];
self.embark.logger.info(`Installing npm package ${npmPackage} ...`);
async.waterfall([
function npmInstallAsync(cb) {
utils.runCmd(npmInstall.join(' '), {silent: false, exitOnError: false}, (err) => {
if(err) {
return cb(err);
}
cb();
});
},
function addToEmbarkConfig(cb) {
// get the installed package from package.json
let packageFile = fs.readJSONSync(self.config.packageFile);
let dependencies = Object.keys(packageFile.dependencies);
let installedPackage = dependencies.filter((dep) => npmPackage.indexOf(dep) >=0);
self.embarkConfig.plugins[installedPackage[0]] = {};
fs.writeFile(self.config.embarkConfigFile, JSON.stringify(self.embarkConfig, null, 2), cb);
}
], (err) => {
if(err) {
let errorMessage = `Error installing npm package ${npmPackage}. Please visit https://embark.status.im/plugins/ for all supported plugins`;
self.embark.logger.error(errorMessage);
return callback('Error occurred');
}
callback(null, `npm package ${npmPackage} successfully installed as a plugin`);
});
}
};
});
}
}
module.exports = PluginCommand;

View File

@ -1,6 +1,6 @@
/*global web3 */ /*global web3 */
let __embarkSwarm = {}; let __embarkSwarm = {_swarmConnection: undefined};
const bytes = require("eth-lib/lib/bytes"); import SwarmAPI from 'swarm-api';
__embarkSwarm.setProvider = function (options) { __embarkSwarm.setProvider = function (options) {
let protocol = options.protocol || 'http'; let protocol = options.protocol || 'http';
@ -13,10 +13,10 @@ __embarkSwarm.setProvider = function (options) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
try { try {
if (!web3.bzz.currentProvider && !options.useOnlyGivenProvider) { if (!web3.bzz.currentProvider && !options.useOnlyGivenProvider) {
web3.bzz.setProvider(this._connectUrl); this._swarmConnection = new SwarmAPI({gateway: this._connectUrl});
} }
else if(options.useOnlyGivenProvider && web3.bzz.givenProvider !== null){ else if (options.useOnlyGivenProvider && web3.bzz.givenProvider !== null) {
web3.bzz.setProvider(web3.bzz.givenProvider); this._swarmConnection = new SwarmAPI({gateway: web3.bzz.givenProvider});
} }
resolve(this); resolve(this);
} catch (err) { } catch (err) {
@ -29,22 +29,21 @@ __embarkSwarm.setProvider = function (options) {
__embarkSwarm.isAvailable = function () { __embarkSwarm.isAvailable = function () {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
// if web3 swarm object doesn't exist // if web3 swarm object doesn't exist
if (!web3.bzz) { if (!this._swarmConnection) {
return resolve(false); return resolve(false);
} }
// swarm obj exists, but has no provider set (seems to happen a LOT!), // swarm obj exists, but has no provider set (seems to happen a LOT!),
// try setting the provider to our currently set provider again // try setting the provider to our currently set provider again
else if(!web3.bzz.currentProvider && this._config.host){ else if (!this._swarmConnection.gateway && this._config.host) {
web3.bzz.setProvider(this._connectUrl); this._swarmConnection.gateway = this._connectUrl;
} }
if (!web3.bzz.currentProvider) { if (!this._swarmConnection.gateway) {
return resolve(false); return resolve(false);
} }
web3.bzz.isAvailable() this._swarmConnection.isAvailable((err, isAvailable) => {
.then(resolve) if (err) return reject(err);
.catch(() => { resolve(isAvailable);
reject(this._connectError); });
});
}); });
}; };
@ -54,10 +53,11 @@ __embarkSwarm.saveText = function (text) {
if (!isAvailable) { if (!isAvailable) {
return reject(this._connectError); return reject(this._connectError);
} }
web3.bzz.upload(text) this._swarmConnection.uploadRaw(text, (err, hash) => {
.then(resolve) if (err) return reject(err);
.catch(reject); resolve(hash);
}).catch(reject); });
});
}); });
}; };
@ -67,10 +67,11 @@ __embarkSwarm.get = function (hash) {
if (!isAvailable) { if (!isAvailable) {
return reject(this._connectError); return reject(this._connectError);
} }
web3.bzz.download(hash) this._swarmConnection.downloadRaw(hash, (err, content) => {
.then((uint8Array) => resolve(bytes.toString(bytes.fromUint8Array(uint8Array)))) if (err) return reject(err);
.catch(reject); resolve(content);
}).catch(reject); });
});
}); });
}; };
@ -89,10 +90,11 @@ __embarkSwarm.uploadFile = function (inputSelector) {
if (!isAvailable) { if (!isAvailable) {
return reject(this._connectError); return reject(this._connectError);
} }
web3.bzz.upload(fileContent) this._swarmConnection.uploadRaw(fileContent, (err, hash) => {
.then(resolve) if (err) return reject(err);
.catch(reject); resolve(hash);
}).catch(reject); });
});
}; };
reader.onerror = reject; reader.onerror = reject;
reader.readAsArrayBuffer(file); reader.readAsArrayBuffer(file);
@ -100,7 +102,7 @@ __embarkSwarm.uploadFile = function (inputSelector) {
}; };
__embarkSwarm.getUrl = function (hash) { __embarkSwarm.getUrl = function (hash) {
return `${this._config.getUrl || this._connectUrl + '/bzz:/'}${hash}`; return `${this._connectUrl}/bzz-raw:/${hash}`;
}; };
const NotAvailable = "Not available with Swarm"; const NotAvailable = "Not available with Swarm";

View File

@ -1,9 +1,10 @@
const UploadSwarm = require('./upload.js'); const UploadSwarm = require('./upload.js');
const utils = require('../../utils/utils.js'); const utils = require('../../utils/utils.js');
const fs = require('../../core/fs.js'); const fs = require('../../core/fs.js');
const Web3Bzz = require('web3-bzz'); const SwarmAPI = require('swarm-api');
// TODO: not great, breaks module isolation // TODO: not great, breaks module isolation
const StorageProcessesLauncher = require('../storage/storageProcessesLauncher'); const StorageProcessesLauncher = require('../storage/storageProcessesLauncher');
const constants = require('../../constants.json');
class Swarm { class Swarm {
@ -27,23 +28,30 @@ class Swarm {
return; return;
} }
this.bzz = new Web3Bzz(this.providerUrl); this.swarm = new SwarmAPI({gateway: this.providerUrl});
this.setServiceCheck(); this.setServiceCheck();
this.addProviderToEmbarkJS(); this.addProviderToEmbarkJS();
// TODO add check to see if we need to start process this.addObjectToConsole();
this.startProcess(() => {});
this.registerUploadCommand(); this.registerUploadCommand();
this._checkService((err) => { // swarm needs geth to be running first
if (!err) { this.events.once(constants.blockchain.blockchainReady, () => {
return; this.swarm.isAvailable((err, isAvailable) => {
} if (!err || isAvailable) {
self.logger.info("Swarm node not found, attempting to start own node"); this.logger.info("Swarm node found, using currently running node");
self.startProcess(() => {}); return;
}
this.logger.info("SWARM: Swarm node not found, attempting to start own node");
return this.startProcess(() => {});
});
}); });
} }
addObjectToConsole() {
this.events.emit("runcode:register", "swarm", this.swarm);
}
setServiceCheck() { setServiceCheck() {
let self = this; let self = this;
@ -55,23 +63,21 @@ class Swarm {
self.logger.info(__('Swarm node is offline...')); self.logger.info(__('Swarm node is offline...'));
}); });
self.events.request("services:register", 'Swarm', function(cb){ self.events.request("services:register", 'Swarm', function (cb) {
self.logger.trace(`Checking Swarm availability on ${self.bzz.currentProvider}...`); self.logger.trace(`Checking Swarm availability on ${self.providerUrl}...`);
self._checkService((err, result) => { self._checkService((err, result) => {
if (err) { if (err) {
self.logger.trace("Check Swarm availability error: " + err); self.logger.trace("Check Swarm availability error: " + err);
return cb({name: "Swarm ", status: 'off'}); return cb({name: "Swarm ", status: 'off'});
} }
self.logger.trace("Swarm " + (result ? '':'on') + "available"); self.logger.trace("Swarm " + (result ? '' : 'un') + "available");
return cb({name: "Swarm ", status: result ? 'on':'off'}); return cb({name: "Swarm ", status: result ? 'on' : 'off'});
}); });
}); });
} }
_checkService(cb) { _checkService(cb) {
this.bzz.isAvailable().then(result => { this.swarm.isAvailable(cb);
cb(null, result);
}).catch(cb);
} }
addProviderToEmbarkJS() { addProviderToEmbarkJS() {
@ -101,8 +107,8 @@ class Swarm {
let upload_swarm = new UploadSwarm({ let upload_swarm = new UploadSwarm({
buildDir: self.buildDir || 'dist/', buildDir: self.buildDir || 'dist/',
storageConfig: self.storageConfig, storageConfig: self.storageConfig,
getUrl: self.getUrl, providerUrl: self.providerUrl,
bzz: self.bzz swarm: self.swarm
}); });
upload_swarm.deploy(cb); upload_swarm.deploy(cb);

View File

@ -5,33 +5,26 @@ class Swarm {
constructor(options) { constructor(options) {
this.options = options; this.options = options;
this.buildDir = options.buildDir || 'dist/'; this.buildDir = options.buildDir || 'dist/';
this.bzz = options.bzz; this.swarm = options.swarm;
this.getUrl = options.getUrl; this.providerUrl = options.providerUrl;
} }
deploy(cb) { deploy(cb) {
console.log(__("deploying to swarm!")); console.log(__("deploying to swarm!"));
let self = this; const self = this;
let bzz = this.bzz; const swarm = this.swarm;
async.waterfall([ async.waterfall([
function runCommand(callback) { function runCommand(callback) {
console.log(("=== " + __("adding %s to swarm", self.buildDir)).green); console.log(("=== " + __("adding %s to swarm", self.buildDir)).green);
bzz.upload({ swarm.uploadDirectory(self.buildDir, 'index.html', callback);
path: self.buildDir, // path to data / file / directory
kind: "directory", // could also be "file" or "data"
defaultFile: "index.html" // optional, and only for kind === "directory"
})
.then((success) => {
callback(null, success);
})
.catch(callback);
}, },
function printUrls(dir_hash, callback) { function printUrls(dir_hash, callback) {
if (!dir_hash) { if (!dir_hash) {
return callback('No directory hash was returned'); return callback('No directory hash was returned');
} }
console.log(("=== " + __("DApp available at") + ` ${self.getUrl}${dir_hash}/`).green); console.log(("=== " + __("DApp available at") + ` ${self.providerUrl}/bzz:/${dir_hash}/index.html`).green);
console.log(("=== " + __("DApp available at") + ` https://swarm-gateways.net/bzz:/${dir_hash}`).green); console.log(("=== " + __("DApp available at") + ` https://swarm-gateways.net/bzz:/${dir_hash}/index.html`).green);
console.log(("=== " + __("NOTE: Swarm AND a blockchain node must be running for the dApp to work correctly (ie 'embark run')").yellow));
callback(null, dir_hash); callback(null, dir_hash);
} }

View File

@ -99,7 +99,42 @@ class Test {
if (!this.sim) { if (!this.sim) {
this.sim = getSimulator(); this.sim = getSimulator();
} }
this.web3.setProvider(this.sim.provider(this.simOptions));
let simProvider = this.sim.provider(this.simOptions);
// Here we patch the sendAsync method on the provider. The goal behind this is to force pure/constant/view calls to become
// transactions, so that we can pull in execution traces and account for those executions in code coverage.
//
// Instead of a simple call, here's what happens:
//
// 1) A transaction is sent with the same payload, and a pre-defined gas price;
// 2) We wait for the transaction to be mined by asking for the receipt;
// 3) Once we get the receipt back, we dispatch the real call and pass the original callback;
//
// This will still allow tests to get the return value from the call and run contracts unmodified.
simProvider.realSendAsync = simProvider.sendAsync.bind(simProvider);
simProvider.sendAsync = function(payload, cb) {
if(payload.method !== 'eth_call') {
return simProvider.realSendAsync(payload, cb);
}
let newParams = Object.assign({}, payload.params[0], {gasPrice: '0x77359400'});
let newPayload = {
id: payload.id + 1,
method: 'eth_sendTransaction',
params: [newParams],
jsonrpc: payload.jsonrpc
};
simProvider.realSendAsync(newPayload, (_err, response) => {
let txHash = response.result;
self.web3.eth.getTransactionReceipt(txHash, (_err, _res) => {
simProvider.realSendAsync(payload, cb);
});
});
};
this.web3.setProvider(simProvider);
callback(); callback();
} }
@ -144,26 +179,28 @@ class Test {
self.engine.startService("codeCoverage"); self.engine.startService("codeCoverage");
if (self.options.node === 'embark') { if (self.options.node === 'embark') {
return self.engine.ipc.connect((err) => { if (!self.engine.ipc.connected) {
if (err) { self.engine.logger.error("Could not connect to Embark's IPC. Is embark running?");
this.engine.logger.error(err.message || err); process.exit(1);
this.engine.logger.error("Could not connect to Embark's IPC. Is embark running?"); }
process.exit(1); return self.connectToIpcNode(cb);
}
self.engine.ipc.request('blockchain:node', {}, (err, node) => {
if (err) {
return self.engine.logger.error(err.message || err);
}
self.options.node = node;
cb();
});
});
} }
cb(); cb();
} }
], callback); ], callback);
} }
connectToIpcNode(cb) {
this.engine.ipc.request('blockchain:node', {}, (err, node) => {
if (err) {
this.engine.logger.error(err.message || err);
return cb();
}
this.options.node = node;
cb();
});
}
onReady(callback) { onReady(callback) {
const self = this; const self = this;
if (this.ready) { if (this.ready) {

View File

@ -22,7 +22,13 @@ class TemplateGenerator {
console.log(__('Installing Template from ' + uri + '....').green); console.log(__('Installing Template from ' + uri + '....').green);
fs.mkdirpSync(utils.dirname(tmpFilePath)); fs.mkdirpSync(utils.dirname(tmpFilePath));
utils.downloadFile(url, tmpFilePath, () => { utils.downloadFile(url, tmpFilePath, (err) => {
if (err) {
console.error(err.red);
console.error('Does the template really exist?'.red);
console.error(`Embark's supported templates: https://embark.status.im/templates/`.green);
process.exit(1);
}
utils.extractZip(tmpFilePath, fspath, { utils.extractZip(tmpFilePath, fspath, {
map: file => { map: file => {
let fixed_path = file.path.split('/'); let fixed_path = file.path.split('/');

View File

@ -129,17 +129,37 @@ function pingEndpoint(host, port, type, protocol, origin, callback) {
}); });
} }
function runCmd(cmd, options) { function runCmd(cmd, options, callback) {
const shelljs = require('shelljs'); const shelljs = require('shelljs');
let result = shelljs.exec(cmd, options || {silent: true}); options = Object.assign({silent: true, exitOnError: true, async: true}, options || {});
if (result.code !== 0) { const outputToConsole = !options.silent;
console.log("error doing.. " + cmd); options.silent = true;
console.log(result.output); let result = shelljs.exec(cmd, options, function (code, stdout) {
if (result.stderr !== undefined) { if(code !== 0) {
console.log(result.stderr); if (options.exitOnError) {
return exit();
}
if(typeof callback === 'function') {
callback(`shell returned code ${code}`);
}
} else {
if(typeof callback === 'function') {
return callback(null, stdout);
}
} }
exit(); });
}
result.stdout.on('data', function(data) {
if(outputToConsole) {
console.log(data);
}
});
result.stderr.on('data', function(data) {
if (outputToConsole) {
console.log(data);
}
});
} }
function cd(folder) { function cd(folder) {
@ -160,13 +180,17 @@ function downloadFile(url, dest, cb) {
const o_fs = require('fs-extra'); const o_fs = require('fs-extra');
var file = o_fs.createWriteStream(dest); var file = o_fs.createWriteStream(dest);
(url.substring(0, 5) === 'https' ? https : http).get(url, function (response) { (url.substring(0, 5) === 'https' ? https : http).get(url, function (response) {
if (response.statusCode !== 200) {
cb(`Download failed, response code ${response.statusCode}`);
return;
}
response.pipe(file); response.pipe(file);
file.on('finish', function () { file.on('finish', function () {
file.close(cb); file.close(cb);
}); });
}).on('error', function (err) { }).on('error', function (err) {
o_fs.unlink(dest); o_fs.unlink(dest);
if (cb) cb(err.message); cb(err.message);
}); });
} }

4314
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -81,6 +81,7 @@
"solc": "0.4.24", "solc": "0.4.24",
"string-replace-async": "^1.2.1", "string-replace-async": "^1.2.1",
"style-loader": "^0.19.0", "style-loader": "^0.19.0",
"swarm-api": "^0.1.2",
"tar": "^3.1.5", "tar": "^3.1.5",
"toposort": "^1.0.0", "toposort": "^1.0.0",
"underscore": "^1.9.0", "underscore": "^1.9.0",

View File

@ -26,7 +26,12 @@ module.exports = {
proxy: true, // Proxy is used to present meaningful information about transactions proxy: true, // Proxy is used to present meaningful information about transactions
targetGasLimit: 8000000, // Target gas limit sets the artificial target gas floor for the blocks to mine targetGasLimit: 8000000, // Target gas limit sets the artificial target gas floor for the blocks to mine
simulatorMnemonic: "example exile argue silk regular smile grass bomb merge arm assist farm", // Mnemonic used by the simulator to generate a wallet simulatorMnemonic: "example exile argue silk regular smile grass bomb merge arm assist farm", // Mnemonic used by the simulator to generate a wallet
simulatorBlocktime: 0 // Specify blockTime in seconds for automatic mining. Default is 0 and no auto-mining. simulatorBlocktime: 0, // Specify blockTime in seconds for automatic mining. Default is 0 and no auto-mining.
account: {
// numAccounts: 3, // When specified, creates accounts for use in the dapp. This option only works in the development environment, and can be used as a quick start option that bypasses the need for MetaMask in development. These accounts are unlocked and funded with the below settings.
// password: "config/development/password", // Password for the created accounts (as specified in the `numAccounts` setting)
// balance: "5 ether" // Balance to be given to the created accounts (as specified in the `numAccounts` setting)
}
}, },
// merges with the settings in default // merges with the settings in default

View File

@ -1,6 +1,7 @@
module.exports = { module.exports = {
// default applies to all environments // default applies to all environments
default: { default: {
enabled: true,
available_providers: ["ens"], available_providers: ["ens"],
provider: "ens" provider: "ens"
}, },

View File

@ -21,16 +21,18 @@ class App extends React.Component {
error: null, error: null,
activeKey: 1, activeKey: 1,
whisperEnabled: false, whisperEnabled: false,
storageEnabled: false storageEnabled: false,
blockchainEnabled: false
}; };
} }
componentDidMount() { componentDidMount() {
EmbarkJS.onReady((err) => { EmbarkJS.onReady((err) => {
this.setState({blockchainEnabled: true});
if (err) { if (err) {
// If err is not null then it means something went wrong connecting to ethereum // If err is not null then it means something went wrong connecting to ethereum
// you can use this to ask the user to enable metamask for e.g // you can use this to ask the user to enable metamask for e.g
return this.setState({error: err}); return this.setState({error: err.message || err});
} }
if (EmbarkJS.isNewWeb3()) { if (EmbarkJS.isNewWeb3()) {
EmbarkJS.Messages.Providers.whisper.getWhisperVersion((err, _version) => { EmbarkJS.Messages.Providers.whisper.getWhisperVersion((err, _version) => {
@ -81,7 +83,7 @@ class App extends React.Component {
return (<div> return (<div>
<h3>Embark - Usage Example</h3> <h3>Embark - Usage Example</h3>
<Tabs onSelect={this.handleSelect} activeKey={this.state.activeKey} id="uncontrolled-tab-example"> <Tabs onSelect={this.handleSelect} activeKey={this.state.activeKey} id="uncontrolled-tab-example">
<Tab eventKey={1} title="Blockchain"> <Tab eventKey={1} title={this._renderStatus('Blockchain', this.state.blockchainEnabled)}>
<Blockchain/> <Blockchain/>
</Tab> </Tab>
<Tab eventKey={2} title={this._renderStatus('Decentralized Storage', this.state.storageEnabled)}> <Tab eventKey={2} title={this._renderStatus('Decentralized Storage', this.state.storageEnabled)}>

View File

@ -26,7 +26,12 @@ module.exports = {
proxy: true, // Proxy is used to present meaningful information about transactions proxy: true, // Proxy is used to present meaningful information about transactions
targetGasLimit: 8000000, // Target gas limit sets the artificial target gas floor for the blocks to mine targetGasLimit: 8000000, // Target gas limit sets the artificial target gas floor for the blocks to mine
simulatorMnemonic: "example exile argue silk regular smile grass bomb merge arm assist farm", // Mnemonic used by the simulator to generate a wallet simulatorMnemonic: "example exile argue silk regular smile grass bomb merge arm assist farm", // Mnemonic used by the simulator to generate a wallet
simulatorBlocktime: 0 // Specify blockTime in seconds for automatic mining. Default is 0 and no auto-mining. simulatorBlocktime: 0, // Specify blockTime in seconds for automatic mining. Default is 0 and no auto-mining.
account: {
// numAccounts: 3, // When specified, creates accounts for use in the dapp. This option only works in the development environment, and can be used as a quick start option that bypasses the need for MetaMask in development. These accounts are unlocked and funded with the below settings.
// password: "config/development/password", // Password for the created accounts (as specified in the `numAccounts` setting)
// balance: "5 ether" // Balance to be given to the created accounts (as specified in the `numAccounts` setting)
}
}, },
// merges with the settings in default // merges with the settings in default

View File

@ -1,6 +1,7 @@
module.exports = { module.exports = {
// default applies to all environments // default applies to all environments
default: { default: {
enabled: true,
available_providers: ["ens"], available_providers: ["ens"],
provider: "ens" provider: "ens"
}, },

View File

@ -1,26 +0,0 @@
pragma solidity ^0.4.18;
interface ENS {
// Logged when the owner of a node assigns a new owner to a subnode.
event NewOwner(bytes32 indexed node, bytes32 indexed label, address owner);
// Logged when the owner of a node transfers ownership to a new account.
event Transfer(bytes32 indexed node, address owner);
// Logged when the resolver for a node changes.
event NewResolver(bytes32 indexed node, address resolver);
// Logged when the TTL of a node changes
event NewTTL(bytes32 indexed node, uint64 ttl);
function setSubnodeOwner(bytes32 node, bytes32 label, address owner) external;
function setResolver(bytes32 node, address resolver) external;
function setOwner(bytes32 node, address owner) external;
function setTTL(bytes32 node, uint64 ttl) external;
function owner(bytes32 node) external view returns (address);
function resolver(bytes32 node) external view returns (address);
function ttl(bytes32 node) external view returns (uint64);
}

View File

@ -1,99 +0,0 @@
pragma solidity ^0.4.18;
import './ENS.sol';
/**
* The ENS registry contract.
*/
contract ENSRegistry is ENS {
struct Record {
address owner;
address resolver;
uint64 ttl;
}
mapping (bytes32 => Record) records;
// Permits modifications only by the owner of the specified node.
modifier only_owner(bytes32 node, address owner) {
require(records[node].owner == 0 || records[node].owner == msg.sender || records[node].owner == owner);
_;
}
/**
* @dev Constructs a new ENS registrar.
*/
constructor() public {
records[0x0].owner = msg.sender;
}
/**
* @dev Transfers ownership of a node to a new address. May only be called by the current owner of the node.
* @param node The node to transfer ownership of.
* @param owner The address of the new owner.
*/
function setOwner(bytes32 node, address owner) public only_owner(node, owner) {
emit Transfer(node, owner);
records[node].owner = owner;
}
/**
* @dev Transfers ownership of a subnode sha3(node, label) to a new address. May only be called by the owner of the parent node.
* @param node The parent node.
* @param label The hash of the label specifying the subnode.
* @param owner The address of the new owner.
*/
function setSubnodeOwner(bytes32 node, bytes32 label, address owner) public only_owner(node, owner) {
bytes32 subnode = keccak256(abi.encodePacked(node, label));
emit NewOwner(node, label, owner);
records[subnode].owner = owner;
}
/**
* @dev Sets the resolver address for the specified node.
* @param node The node to update.
* @param resolver The address of the resolver.
*/
function setResolver(bytes32 node, address resolver) public only_owner(node, 0x0) {
emit NewResolver(node, resolver);
records[node].resolver = resolver;
}
/**
* @dev Sets the TTL for the specified node.
* @param node The node to update.
* @param ttl The TTL in seconds.
*/
function setTTL(bytes32 node, uint64 ttl) public only_owner(node, 0x0) {
emit NewTTL(node, ttl);
records[node].ttl = ttl;
}
/**
* @dev Returns the address that owns the specified node.
* @param node The specified node.
* @return address of the owner.
*/
function owner(bytes32 node) public view returns (address) {
return records[node].owner;
}
/**
* @dev Returns the address of the resolver for the specified node.
* @param node The specified node.
* @return address of the resolver.
*/
function resolver(bytes32 node) public view returns (address) {
return records[node].resolver;
}
/**
* @dev Returns the TTL of a node, and any records associated with it.
* @param node The specified node.
* @return ttl of the node.
*/
function ttl(bytes32 node) public view returns (uint64) {
return records[node].ttl;
}
}

View File

@ -1,38 +0,0 @@
pragma solidity ^0.4.18;
import './ENS.sol';
import './Resolver.sol';
/**
* A registrar that allocates subdomains to the first person to claim them.
*/
contract FIFSRegistrar {
ENS ens;
bytes32 rootNode;
modifier only_owner(bytes32 subnode) {
bytes32 node = keccak256(abi.encodePacked(rootNode, subnode));
address currentOwner = ens.owner(node);
require(currentOwner == 0 || currentOwner == msg.sender);
_;
}
/**
* Constructor.
* @param ensAddr The address of the ENS registry.
* @param node The node that this registrar administers.
*/
constructor(ENS ensAddr, bytes32 node) public {
ens = ensAddr;
rootNode = node;
}
/**
* Register a name, or change the owner of an existing registration.
* @param subnode The hash of the label to register.
* @param owner The address of the new owner.
*/
function register(bytes32 subnode, address owner) public only_owner(subnode) {
ens.setSubnodeOwner(rootNode, subnode, owner);
}
}

View File

@ -1,191 +0,0 @@
pragma solidity ^0.4.23;
import "./ENS.sol";
/**
* A simple resolver anyone can use; only allows the owner of a node to set its
* address.
*/
contract Resolver {
event AddrChanged(bytes32 indexed node, address a);
event ContentChanged(bytes32 indexed node, bytes32 hash);
event NameChanged(bytes32 indexed node, string name);
event ABIChanged(bytes32 indexed node, uint256 indexed contentType);
event PubkeyChanged(bytes32 indexed node, bytes32 x, bytes32 y);
event TextChanged(bytes32 indexed node, string indexedKey, string key);
struct PublicKey {
bytes32 x;
bytes32 y;
}
struct Record {
address addr;
bytes32 content;
string name;
PublicKey pubkey;
mapping(string=>string) text;
mapping(uint256=>bytes) abis;
}
ENS ens;
mapping (bytes32 => Record) records;
modifier only_owner(bytes32 node) {
address currentOwner = ens.owner(node);
require(currentOwner == 0 || currentOwner == msg.sender);
_;
}
/**
* Constructor.
* @param ensAddr The ENS registrar contract.
*/
constructor(ENS ensAddr) public {
ens = ensAddr;
}
/**
* Sets the address associated with an ENS node.
* May only be called by the owner of that node in the ENS registry.
* @param node The node to update.
* @param addr The address to set.
*/
function setAddr(bytes32 node, address addr) public only_owner(node) {
records[node].addr = addr;
emit AddrChanged(node, addr);
}
/**
* Sets the content hash associated with an ENS node.
* May only be called by the owner of that node in the ENS registry.
* Note that this resource type is not standardized, and will likely change
* in future to a resource type based on multihash.
* @param node The node to update.
* @param hash The content hash to set
*/
function setContent(bytes32 node, bytes32 hash) public only_owner(node) {
records[node].content = hash;
emit ContentChanged(node, hash);
}
/**
* Sets the name associated with an ENS node, for reverse records.
* May only be called by the owner of that node in the ENS registry.
* @param node The node to update.
* @param name The name to set.
*/
function setName(bytes32 node, string name) public only_owner(node) {
records[node].name = name;
emit NameChanged(node, name);
}
/**
* Sets the ABI associated with an ENS node.
* Nodes may have one ABI of each content type. To remove an ABI, set it to
* the empty string.
* @param node The node to update.
* @param contentType The content type of the ABI
* @param data The ABI data.
*/
function setABI(bytes32 node, uint256 contentType, bytes data) public only_owner(node) {
// Content types must be powers of 2
require(((contentType - 1) & contentType) == 0);
records[node].abis[contentType] = data;
emit ABIChanged(node, contentType);
}
/**
* Sets the SECP256k1 public key associated with an ENS node.
* @param node The ENS node to query
* @param x the X coordinate of the curve point for the public key.
* @param y the Y coordinate of the curve point for the public key.
*/
function setPubkey(bytes32 node, bytes32 x, bytes32 y) public only_owner(node) {
records[node].pubkey = PublicKey(x, y);
emit PubkeyChanged(node, x, y);
}
/**
* Sets the text data associated with an ENS node and key.
* May only be called by the owner of that node in the ENS registry.
* @param node The node to update.
* @param key The key to set.
* @param value The text data value to set.
*/
function setText(bytes32 node, string key, string value) public only_owner(node) {
records[node].text[key] = value;
emit TextChanged(node, key, key);
}
/**
* Returns the text data associated with an ENS node and key.
* @param node The ENS node to query.
* @param key The text data key to query.
* @return The associated text data.
*/
function text(bytes32 node, string key) public view returns (string) {
return records[node].text[key];
}
/**
* Returns the SECP256k1 public key associated with an ENS node.
* Defined in EIP 619.
* @param node The ENS node to query
* @return x, y the X and Y coordinates of the curve point for the public key.
*/
function pubkey(bytes32 node) public view returns (bytes32 x, bytes32 y) {
return (records[node].pubkey.x, records[node].pubkey.y);
}
/**
* Returns the ABI associated with an ENS node.
* Defined in EIP205.
* @param node The ENS node to query
* @param contentTypes A bitwise OR of the ABI formats accepted by the caller.
* @return contentType The content type of the return value
* @return data The ABI data
*/
function ABI(bytes32 node, uint256 contentTypes) public view returns (uint256 contentType, bytes data) {
Record storage record = records[node];
for (contentType = 1; contentType <= contentTypes; contentType <<= 1) {
if ((contentType & contentTypes) != 0 && record.abis[contentType].length > 0) {
data = record.abis[contentType];
return;
}
}
contentType = 0;
}
/**
* Returns the name associated with an ENS node, for reverse records.
* Defined in EIP181.
* @param node The ENS node to query.
* @return The associated name.
*/
function name(bytes32 node) public view returns (string) {
return records[node].name;
}
/**
* Returns the content hash associated with an ENS node.
* Note that this resource type is not standardized, and will likely change
* in future to a resource type based on multihash.
* @param node The ENS node to query.
* @return The associated content hash.
*/
function content(bytes32 node) public view returns (bytes32) {
return records[node].content;
}
/**
* Returns the address associated with an ENS node.
* @param node The ENS node to query.
* @return The associated address.
*/
function addr(bytes32 node) public view returns (address) {
return records[node].addr;
}
}

View File

@ -1,6 +1,7 @@
pragma solidity ^0.4.17; pragma solidity ^0.4.17;
import "https://github.com/embark-framework/embark/blob/develop/test_apps/contracts_app/contracts/ownable.sol"; import "https://github.com/embark-framework/embark/blob/develop/test_apps/contracts_app/contracts/ownable.sol";
import "https://github.com/embark-framework/embark/blob/develop/test_apps/contracts_app/contracts/contract_args.sol";
contract SimpleStorageWithHttpImport is Ownable { contract SimpleStorageWithHttpImport is Ownable {

View File

@ -72,15 +72,6 @@ module.exports = {
SimpleStorageWithHttpImport: { SimpleStorageWithHttpImport: {
fromIndex: 0, fromIndex: 0,
args: [100] args: [100]
},
ENSRegistry: {
"deploy": false
},
Resolver: {
"deploy": false
},
FIFSRegistrar: {
"deploy": false
} }
}, },
afterDeploy: [ afterDeploy: [

View File

@ -1,43 +0,0 @@
/*global contract, config, it, assert, before*/
const Resolver = require('Embark/contracts/Resolver');
const namehash = require('eth-ens-namehash');
const address = '0x38ac14a9B6a7c8F9C46e4804074186c9F201D0A0';
const rootNode = namehash.hash('embark.eth');
config({
contracts: {
"ENSRegistry": {
"args": []
},
"Resolver": {
"args": ["$ENSRegistry"]
},
"FIFSRegistrar": {
"args": ["$ENSRegistry", rootNode],
"onDeploy": [
`ENSRegistry.methods.setOwner('${rootNode}', web3.eth.defaultAccount).send().then(() => {
ENSRegistry.methods.setResolver('${rootNode}', "$Resolver").send();
Resolver.methods.setAddr('${rootNode}', '${address}').send();
});`
]
}
}
});
contract("ENS", function () {
it("should have registered embark.eth", function () {
let maxRetry = 20;
let domainAddress;
const wait = setInterval(async () => {
domainAddress = await Resolver.methods.addr(rootNode).call();
if (domainAddress || maxRetry === 0) {
clearInterval(wait);
assert.strictEqual(domainAddress, address);
return;
}
maxRetry--;
}, 50);
});
});

View File

@ -1,22 +1,34 @@
/*global contract, it, embark, assert, before*/ /*global contract, it, embark, assert, before, web3*/
const SimpleStorage = embark.require('Embark/contracts/SimpleStorage'); const SimpleStorage = embark.require('Embark/contracts/SimpleStorage');
const Utils = require('embarkjs').Utils;
contract("SimpleStorage Deploy", function () { contract("SimpleStorage Deploy", function () {
let SimpleStorageInstance; let simpleStorageInstance;
before(function(done) {
before(async function() { Utils.secureSend(web3, SimpleStorage.deploy({arguments: [150]}), {}, true, function(err, receipt) {
SimpleStorageInstance = await SimpleStorage.deploy({arguments: [150]}).send(); if(err) {
return done(err);
}
simpleStorageInstance = SimpleStorage;
simpleStorageInstance.options.address = receipt.contractAddress;
done();
});
}); });
it("should set constructor value", async function () { it("should set constructor value", async function () {
let result = await SimpleStorageInstance.methods.storedData().call(); let result = await simpleStorageInstance.methods.storedData().call();
assert.strictEqual(parseInt(result, 10), 150); assert.strictEqual(parseInt(result, 10), 150);
}); });
it("set storage value", async function () { it("set storage value", function (done) {
await SimpleStorageInstance.methods.set(150).send(); Utils.secureSend(web3, simpleStorageInstance.methods.set(200), {}, false, async function(err) {
let result = await SimpleStorageInstance.methods.get().call(); if (err) {
assert.strictEqual(parseInt(result, 10), 150); return done(err);
}
let result = await simpleStorageInstance.methods.get().call();
assert.strictEqual(parseInt(result, 10), 200);
done();
});
}); });
}); });