Merge pull request #913 from embark-framework/next

3.2 merge
This commit is contained in:
Iuri Matias 2018-09-26 15:38:01 -04:00 committed by GitHub
commit 142f04df06
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
280 changed files with 22155 additions and 14010 deletions

View File

@ -113,7 +113,7 @@
"no-continue": "off",
"no-div-regex": "error",
"no-duplicate-imports": "error",
"no-else-return": "off",
"no-else-return": 2,
"no-empty-function": "off",
"no-eq-null": "error",
"no-eval": "off",
@ -122,7 +122,12 @@
"no-extra-label": "error",
"no-extra-parens": "off",
"no-floating-decimal": "error",
"no-implicit-coercion": "error",
"no-implicit-coercion": [
"error",
{
"allow": ["!!"]
}
],
"no-implicit-globals": "error",
"no-implied-eval": "error",
"no-inline-comments": "off",
@ -186,7 +191,7 @@
"no-unmodified-loop-condition": "error",
"no-unneeded-ternary": "error",
"no-unused-expressions": "error",
"no-unused-vars": ["error", { "argsIgnorePattern": "^_" }],
"no-unused-vars": ["error", { "argsIgnorePattern": "^_", "varsIgnorePattern": "^_"}],
"no-use-before-define": "off",
"no-useless-call": "off",
"no-useless-computed-key": "error",

24
.github/ISSUE_TEMPLATE.md vendored Normal file
View File

@ -0,0 +1,24 @@
## Overview
**TL;DR**
<One to two sentence description of the issue you are encountering.>
### Extra Detail
#### Steps to reproduce
1.
2.
3.
#### Screenshots
<please upload images of the error to better showcase the problem.>
#### Logs
<please upload logs of the error to better showcase the problem. You can use the following options: --nodashboard --logfile log.txt --loglevel trace>
#### Context (Environment)
* OS:
* Embark Version:
* Node Version:
* NPM Version:
### Questions?
- Leave a comment on this issue! Make sure to use @ mentions if you want a specific person's attention!

11
.github/PULL_REQUEST_TEMPLATE.md vendored Normal file
View File

@ -0,0 +1,11 @@
## Overview
**TL;DR**
<One to two sentence description of the issue you are encountering or trying to solve.>
### Questions
<If relevant, write a list of questions that you would like to discuss related to the changes that you have made.>
### Review
<use @mentions for quick questions, specific feedback, and progress updates.>
### Cool Spaceship Picture

1
.gitignore vendored
View File

@ -20,6 +20,7 @@ test_apps/contracts_app/config/production/password
test_apps/contracts_app/node_modules/
test_apps/contracts_app/chains.json
.idea
.vscode
.eslintrc.json
.embark/
NOTES

2
.npmrc Normal file
View File

@ -0,0 +1,2 @@
engine-strict = true
save-exact = true

View File

@ -1,11 +1,14 @@
language: node_js
os:
- linux
- osx
node_js:
- "8"
- "10"
addons:
code_climate:
repo_token: 7454b1a666015e244c384d19f48c34e35d1ae58c3aa428ec542f10bbcb848358
before_install:
- npm i -g npm@latest
script:
- npm run lint
- npm run test
- npm run testdapp_1
- npm run testdapp_2
- npm test

View File

@ -1,29 +0,0 @@
module.exports = (grunt) ->
grunt.initConfig
"embark-framework": {}
pkg: grunt.file.readJSON('package.json')
clean:
build: ["build/"]
coffee:
compile:
expand: true
src: 'src/**/*.coffee'
dest: 'build/'
ext: '.js'
uglify:
options: banner: '/*! <%= pkg.name %> <%= grunt.template.today("dd-mm-yyyy") %> */\n'
build:
files:
'build/<%= pkg.name %>.min.js': [
"build/<%= pkg.name %>.js"
]
mochaTest:
test:
src: ['test/**/*.js']
grunt.loadTasks "tasks"
require('matchdep').filterAll(['grunt-*','!grunt-cli']).forEach(grunt.loadNpmTasks)
grunt.registerTask 'default', ['clean']
grunt.registerTask 'build', ['clean', 'coffee']

View File

@ -38,9 +38,6 @@ With Embark you can:
```Bash
$ npm -g install embark
# If you plan to use the simulator instead of a real ethereum node.
$ npm -g install ethereumjs-testrpc
```
See [Complete Documentation](https://embark.status.im/docs/).

View File

@ -1,13 +1,15 @@
# Test against the latest version of this Node.js version
environment:
nodejs_version: "8"
matrix:
- nodejs_version: "8"
- nodejs_version: "10"
# Install scripts. (runs after repo cloning)
install:
# Get the latest stable version of Node.js or io.js
- ps: Install-Product node $env:nodejs_version
# install modules
- npm install -g npm@5.7
- npm install -g npm@latest
- npm install
# Post-install test scripts.
@ -15,12 +17,8 @@ test_script:
# Output useful info for debugging.
- node --version
- npm --version
- npm run lint
- npm run test
# tmp fix due to windows npm5 issue
- cd test_apps/test_app/extensions/embark-service && npm install
- npm run testdapp_1
- npm run testdapp_2
# run tests
- npm test
# Don't actually build.
build: off

View File

@ -4,11 +4,12 @@ try {
eval('let __nodeTest = 123;');
} catch(e) {
if (e.name === 'SyntaxError') {
console.error("unsupported version of NodeJS. Make sure you are running nodejs 6.9.5 or above");
console.error("unsupported version of NodeJS. Make sure you are running nodejs 8.11.3 or above");
process.exit();
}
}
var Cmd = require('../lib/cmd');
var Cmd = require('../cmd/cmd');
var cli = new Cmd();
cli.process(process.argv);

View File

@ -1,7 +1,53 @@
const program = require('commander');
const Embark = require('../lib/index');
const i18n = require('./i18n/i18n.js');
let embark = new Embark;
const EmbarkController = require('./cmd_controller.js');
const i18n = require('../lib/core/i18n/i18n.js');
const utils = require('../lib/utils/utils.js');
let embark = new EmbarkController;
// set PWD to process.cwd() since Windows doesn't have a value for PWD
if (!process.env.PWD) {
process.env.PWD = process.cwd();
}
// set the anchor for embark's fs.dappPath()
if (!process.env.DAPP_PATH) {
process.env.DAPP_PATH = process.env.PWD;
}
// set the anchor for embark's fs.embarkPath()
if (!process.env.EMBARK_PATH) {
process.env.EMBARK_PATH = utils.joinPath(__dirname, '..');
}
// NOTE: setting NODE_PATH at runtime won't effect lookup behavior in the
// current process, but will take effect in child processes; this enables
// lookup of *global* embark's own node_modules from within dapp scripts (such
// as an ejected webpack.config.js), making embark's dependencies trasitive
// dependencies of a dapp without the dapp explicitly specifying embark as a
// dependency in the dapp's package.json
process.env.NODE_PATH = utils.joinPath(process.env.EMBARK_PATH, 'node_modules') +
(process.env.NODE_PATH ? require('path').delimiter : '') +
(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 {
constructor() {
@ -13,13 +59,16 @@ class Cmd {
this.demo();
this.build();
this.run();
this.console();
this.blockchain();
this.simulator();
this.test();
this.reset();
this.ejectWebpack();
this.graph();
this.upload();
this.versionCmd();
this.helpCmd();
this.otherCommands();
//If no arguments are passed display help by default
@ -32,7 +81,7 @@ class Cmd {
newApp() {
let validateName = function (value) {
let validateName = function(value) {
try {
if (value.match(/^[a-zA-Z\s-]+$/)) return value;
} catch (e) {
@ -45,14 +94,15 @@ class Cmd {
.description(__('New Application'))
.option('--simple', __('create a barebones project meant only for contract development'))
.option('--locale [locale]', __('language to use (default: en)'))
.action(function (name, options) {
.option('--template <name/url>', __('download a template using a known name or a git host URL'))
.action(function(name, options) {
i18n.setOrDetectLocale(options.locale);
if (name === undefined) {
const promptly = require('promptly');
return promptly.prompt(__("Name your app (default is %s):", 'embarkDapp'), {
default: "embarkDApp",
validator: validateName
}, function (err, inputvalue) {
}, function(err, inputvalue) {
if (err) {
console.error(__('Invalid name') + ':', err.message);
// Manually call retry
@ -63,16 +113,15 @@ class Cmd {
if (options.simple) {
embark.generateTemplate('simple', './', inputvalue);
} else {
embark.generateTemplate('boilerplate', './', inputvalue);
embark.generateTemplate('boilerplate', './', inputvalue, options.template);
}
}
});
}
if (options.simple) {
embark.generateTemplate('simple', './', name);
} else {
if (options.simple) {
embark.generateTemplate('simple', './', name);
} else {
embark.generateTemplate('boilerplate', './', name);
}
embark.generateTemplate('boilerplate', './', name, options.template);
}
});
}
@ -82,7 +131,7 @@ class Cmd {
.command('demo')
.option('--locale [locale]', __('language to use (default: en)'))
.description(__('create a working dapp with a SimpleStorage contract'))
.action(function (options) {
.action(function(options) {
i18n.setOrDetectLocale(options.locale);
embark.generateTemplate('demo', './', 'embark_demo');
});
@ -93,17 +142,20 @@ class Cmd {
.command('build [environment]')
.option('--contracts', 'only compile contracts into Embark wrappers')
.option('--logfile [logfile]', __('filename to output logs (default: none)'))
.option('-c, --client [client]', __('Use a specific ethereum client or simulator (supported: %s)', 'geth, testrpc'))
.option('-c, --client [client]', __('Use a specific ethereum client (supported: %s)', 'geth'))
.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('--pipeline [pipeline]', __('webpack config to use (default: production)'))
.description(__('deploy and build dapp at ') + 'dist/ (default: development)')
.action(function (env, _options) {
.action(function(env, _options) {
checkDeps();
i18n.setOrDetectLocale(_options.locale);
_options.env = env || 'development';
_options.logFile = _options.logfile; // fix casing
_options.logLevel = _options.loglevel; // fix casing
_options.onlyCompile = _options.contracts;
_options.client = _options.client || 'geth';
_options.webpackConfigName = _options.pipeline || 'production';
embark.build(_options);
});
}
@ -112,27 +164,56 @@ class Cmd {
program
.command('run [environment]')
.option('-p, --port [port]', __('port to run the dev webserver (default: %s)', '8000'))
.option('-c, --client [client]', __('Use a specific ethereum client or simulator (supported: %s)', 'geth, testrpc'))
.option('-c, --client [client]', __('Use a specific ethereum client (supported: %s)', 'geth'))
.option('-b, --host [host]', __('host to run the dev webserver (default: %s)', 'localhost'))
.option('--noserver', __('disable the development webserver'))
.option('--nodashboard', __('simple mode, disables the dashboard'))
.option('--nobrowser', __('prevent the development webserver from automatically opening a web browser'))
.option('--no-color', __('no colors in case it\'s needed for compatbility purposes'))
.option('--logfile [logfile]', __('filename to output logs (default: %s)', 'none'))
.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('--pipeline [pipeline]', __('webpack config to use (default: development)'))
.description(__('run dapp (default: %s)', 'development'))
.action(function (env, options) {
.action(function(env, options) {
checkDeps();
i18n.setOrDetectLocale(options.locale);
const nullify = (v) => (!v || typeof v !== 'string') ? null : v;
embark.run({
env: env || 'development',
serverPort: options.port,
serverHost: options.host,
serverPort: nullify(options.port),
serverHost: nullify(options.host),
client: options.client || 'geth',
locale: options.locale,
runWebserver: !options.noserver,
runWebserver: options.noserver == null ? null : !options.noserver,
useDashboard: !options.nodashboard,
logFile: options.logfile,
logLevel: options.loglevel
logLevel: options.loglevel,
webpackConfigName: options.pipeline || 'development',
openBrowser: options.nobrowser == null ? null : !options.nobrowser,
});
});
}
console() {
program
.command('console [environment]')
.option('-c, --client [client]', __('Use a specific ethereum client (supported: %s)', 'geth'))
.option('--logfile [logfile]', __('filename to output logs (default: %s)', 'none'))
.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('--pipeline [pipeline]', __('webpack config to use (default: development)'))
.description(__('Start the Embark console'))
.action(function(env, options) {
checkDeps();
i18n.setOrDetectLocale(options.locale);
embark.console({
env: env || 'development',
client: options.client || 'geth',
locale: options.locale,
logFile: options.logfile,
logLevel: options.loglevel,
webpackConfigName: options.pipeline || 'development'
});
});
}
@ -140,10 +221,11 @@ class Cmd {
blockchain() {
program
.command('blockchain [environment]')
.option('-c, --client [client]', __('Use a specific ethereum client or simulator (supported: %s)', 'geth, testrpc'))
.option('-c, --client [client]', __('Use a specific ethereum client (supported: %s)', 'geth'))
.option('--locale [locale]', __('language to use (default: en)'))
.description(__('run blockchain server (default: %s)', 'development'))
.action(function (env, options) {
.action(function(env, options) {
checkDeps();
i18n.setOrDetectLocale(options.locale);
embark.initConfig(env || 'development', {
embarkConfig: 'embark.json',
@ -165,7 +247,8 @@ class Cmd {
.option('-l, --gasLimit [gasLimit]', __('custom gas limit (default: %s)', '8000000'))
.option('--locale [locale]', __('language to use (default: en)'))
.action(function (env, options) {
.action(function(env, options) {
checkDeps();
i18n.setOrDetectLocale(options.locale);
embark.initConfig(env || 'development', {
embarkConfig: 'embark.json',
@ -184,33 +267,59 @@ class Cmd {
test() {
program
.command('test [file]')
.option('-n , --node <node>', __('node for running the tests ["vm", "embark", <endpoint>] (default: vm)\n') +
' vm - ' + __('start and use an Ethereum simulator (ganache)') + '\n' +
' embark - ' + __('use the node of a running embark process') + '\n' +
' <endpoint> - ' + __('connect to and use the specified node'))
.option('-d , --gasDetails', __('print the gas cost for each contract deployment when running the tests'))
.option('-c , --coverage', __('generate a coverage report after running the tests (vm only)'))
.option('--locale [locale]', __('language to use (default: en)'))
.option('--loglevel [loglevel]', __('level of logging to display') + ' ["error", "warn", "info", "debug", "trace"]', /^(error|warn|info|debug|trace)$/i, 'warn')
.description(__('run tests'))
.action(function (file, options) {
.action(function(file, options) {
const node = options.node || 'vm';
const urlRegexExp = /^(vm|embark|((ws|https?):\/\/([a-zA-Z0-9_.-]*):?([0-9]*)?))$/i;
if (!urlRegexExp.test(node)) {
console.error(`invalid --node option: must be "vm", "embark" or a valid URL\n`.red);
options.outputHelp();
process.exit(1);
}
options.node = node;
if (options.coverage && options.node !== 'vm') {
console.error(`invalid --node option: coverage supports "vm" only\n`.red);
options.outputHelp();
process.exit(1);
}
checkDeps();
i18n.setOrDetectLocale(options.locale);
embark.runTests({file, loglevel: options.loglevel});
embark.runTests({file, loglevel: options.loglevel, gasDetails: options.gasDetails,
node: options.node, coverage: options.coverage});
});
}
upload() {
program
.command('upload [environment]')
//.option('--ens [ensDomain]', __('ENS domain to associate to'))
.option('--logfile [logfile]', __('filename to output logs (default: %s)', 'none'))
.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'))
.option('-c, --client [client]', __('Use a specific ethereum client (supported: %s)', 'geth'))
.option('--pipeline [pipeline]', __('webpack config to use (default: production)'))
.description(__('Upload your dapp to a decentralized storage') + '.')
.action(function (env, _options) {
.action(function(env, _options) {
checkDeps();
i18n.setOrDetectLocale(_options.locale);
if (env === "ipfs" || env === "swarm") {
console.warn(("did you mean " + "embark upload".bold + " ?").underline);
console.warn("In embark 3.1 forwards, the correct command is embark upload <environment> and the provider is configured in config/storage.js");
}
_options.env = env || 'development';
_options.ensDomain = _options.ens;
_options.logFile = _options.logfile; // fix casing
_options.logLevel = _options.loglevel; // fix casing
_options.client = _options.client || 'geth';
_options.webpackConfigName = _options.pipeline || 'production';
embark.upload(_options);
});
}
@ -223,7 +332,8 @@ class Cmd {
.option('--skip-events', __('Graph will not include events'))
.option('--locale [locale]', __('language to use (default: en)'))
.description(__('generates documentation based on the smart contracts configured'))
.action(function (env, options) {
.action(function(env, options) {
checkDeps();
i18n.setOrDetectLocale(options.locale);
embark.graph({
env: env || 'development',
@ -240,7 +350,7 @@ class Cmd {
.command('reset')
.option('--locale [locale]', __('language to use (default: en)'))
.description(__('resets embarks state on this dapp including clearing cache'))
.action(function (options) {
.action(function(options) {
i18n.setOrDetectLocale(options.locale);
embark.initConfig('development', {
embarkConfig: 'embark.json', interceptLogs: false
@ -249,22 +359,49 @@ class Cmd {
});
}
ejectWebpack() {
program
.command('eject-webpack')
.description(__('copy the default webpack config into your dapp for customization'))
.action(function() {
embark.initConfig('development', {
embarkConfig: 'embark.json',
interceptLogs: false
});
embark.ejectWebpack();
});
}
versionCmd() {
program
.command('version')
.description(__('output the version number'))
.action(function () {
console.log(embark.version);
process.exit(0);
});
.command('version')
.description(__('output the version number'))
.action(function() {
console.log(embark.version);
process.exit(0);
});
}
helpCmd() {
program
.command('help')
.description(__('output usage information and help information'))
.action(function() {
console.log("Documentation can be found at: ".green + "https://embark.status.im/docs/".underline.green);
console.log("");
console.log("Have an issue? submit it here: ".green + "https://github.com/embark-framework/embark/issues/new".underline.green);
console.log("or chat with us directly at: ".green + "https://gitter.im/embark-framework/Lobby".underline.green);
program.help();
process.exit(0);
});
}
otherCommands() {
program
.action(function (cmd) {
.action(function(cmd) {
console.log((__('unknown command') + ' "%s"').red, cmd);
let utils = require('./utils/utils.js');
let dictionary = ['new', 'demo', 'build', 'run', 'blockchain', 'simulator', 'test', 'upload', 'version'];
let utils = require('../lib/utils/utils.js');
let dictionary = ['new', 'demo', 'build', 'run', 'blockchain', 'simulator', 'test', 'upload', 'version', 'console', 'eject-webpack', 'graph', 'help', 'reset'];
let suggestion = utils.proposeAlternative(cmd, dictionary);
if (suggestion) {
console.log((__('did you mean') + ' "%s"?').green, suggestion);

551
cmd/cmd_controller.js Normal file
View File

@ -0,0 +1,551 @@
let async = require('async');
const constants = require('../lib/constants');
require('colors');
let version = require('../package.json').version;
class EmbarkController {
constructor(options) {
this.version = version;
this.options = options || {};
}
initConfig(env, options) {
let Events = require('../lib/core/events.js');
let Logger = require('../lib/core/logger.js');
let Config = require('../lib/core/config.js');
this.events = new Events();
this.logger = new Logger({logLevel: 'debug', events: this.events});
this.config = new Config({env: env, logger: this.logger, events: this.events, context: this.context});
this.config.loadConfigFiles(options);
this.plugins = this.config.plugins;
}
blockchain(env, client) {
this.context = [constants.contexts.blockchain];
return require('../lib/modules/blockchain_process/blockchain.js')(this.config.blockchainConfig, client, env).run();
}
simulator(options) {
this.context = options.context || [constants.contexts.simulator, constants.contexts.blockchain];
let Simulator = require('../lib/modules/blockchain_process/simulator.js');
let simulator = new Simulator({
blockchainConfig: this.config.blockchainConfig,
logger: this.logger
});
simulator.run(options);
}
generateTemplate(templateName, destinationFolder, name, url) {
this.context = [constants.contexts.templateGeneration];
let TemplateGenerator = require('../lib/utils/template_generator.js');
let templateGenerator = new TemplateGenerator(templateName);
if (url) {
return templateGenerator.downloadAndGenerate(url, destinationFolder, name);
}
templateGenerator.generate(destinationFolder, name);
}
run(options) {
let self = this;
self.context = options.context || [constants.contexts.run, constants.contexts.build];
let Dashboard = require('./dashboard/dashboard.js');
const webServerConfig = {};
if (options.runWebserver != null) {
webServerConfig.enabled = options.runWebserver;
}
if (options.serverHost != null) {
webServerConfig.host = options.serverHost;
}
if (options.serverPort != null) {
webServerConfig.port = options.serverPort;
}
if (options.openBrowser != null) {
webServerConfig.openBrowser = options.openBrowser;
}
const Engine = require('../lib/core/engine.js');
const engine = new Engine({
env: options.env,
client: options.client,
locale: options.locale,
version: this.version,
embarkConfig: options.embarkConfig || 'embark.json',
logFile: options.logFile,
logLevel: options.logLevel,
context: self.context,
useDashboard: options.useDashboard,
webServerConfig: webServerConfig,
webpackConfigName: options.webpackConfigName,
ipcRole: 'server'
});
async.waterfall([
function initEngine(callback) {
engine.init({}, () => {
if (!options.useDashboard) {
engine.logger.info('========================'.bold.green);
engine.logger.info((__('Welcome to Embark') + ' ' + engine.version).yellow.bold);
engine.logger.info('========================'.bold.green);
}
callback();
});
},
function startDashboard(callback) {
if (!options.useDashboard) {
return callback();
}
let dashboard = new Dashboard({
events: engine.events,
logger: engine.logger,
plugins: engine.plugins,
version: self.version,
env: engine.env
});
dashboard.start(function () {
engine.logger.info(__('dashboard start'));
callback();
});
},
function (callback) {
let pluginList = engine.plugins.listPlugins();
if (pluginList.length > 0) {
engine.logger.info(__("loaded plugins") + ": " + pluginList.join(", "));
}
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");
engine.events.on('check:backOnline:Ethereum', function () {
engine.logger.info(__('Ethereum node detected') + '..');
engine.config.reloadConfig();
engine.events.request('deploy:contracts', function (err) {
if (err) {
return;
}
engine.logger.info(__('Deployment Done'));
});
});
engine.events.on('outputDone', function () {
engine.logger.info((__("Looking for documentation? You can find it at") + " ").cyan + "http://embark.status.im/docs/".green.underline + ".".cyan);
engine.logger.info(__("Ready").underline);
engine.events.emit("status", __("Ready").green);
});
if (webServerConfig.enabled !== false) {
engine.startService("webServer");
}
engine.startService("fileWatcher");
callback();
}
], function (err, _result) {
if (err) {
engine.logger.error(err.message);
engine.logger.info(err.stack);
} else {
engine.events.emit('firstDeploymentDone');
}
});
}
build(options) {
this.context = options.context || [constants.contexts.build];
const Engine = require('../lib/core/engine.js');
const engine = new Engine({
env: options.env,
client: options.client,
locale: options.locale,
version: this.version,
embarkConfig: 'embark.json',
interceptLogs: false,
logFile: options.logFile,
logLevel: options.logLevel,
events: options.events,
logger: options.logger,
config: options.config,
plugins: options.plugins,
context: this.context,
webpackConfigName: options.webpackConfigName
});
async.waterfall([
function initEngine(callback) {
engine.init({}, callback);
},
function startServices(callback) {
let pluginList = engine.plugins.listPlugins();
if (pluginList.length > 0) {
engine.logger.info(__("loaded plugins") + ": " + pluginList.join(", "));
}
engine.startService("processManager");
engine.startService("libraryManager");
engine.startService("codeRunner");
engine.startService("web3");
if (!options.onlyCompile) {
engine.startService("pipeline");
}
engine.startService("deployment", {onlyCompile: options.onlyCompile});
if (!options.onlyCompile) {
engine.startService("storage");
engine.startService("codeGenerator");
}
callback();
},
function deploy(callback) {
engine.events.request('deploy:contracts', function (err) {
callback(err);
});
},
function waitForWriteFinish(callback) {
if (options.onlyCompile) {
engine.logger.info("Finished compiling".underline);
return callback(null, true);
}
engine.logger.info("Finished deploying".underline);
engine.events.on('outputDone', (err) => {
engine.logger.info(__("finished building").underline);
callback(err, true);
});
}
], function (err, canExit) {
if (err) {
engine.logger.error(err.message);
engine.logger.debug(err.stack);
}
// TODO: this should be moved out and determined somewhere else
if (canExit || !engine.config.contractsConfig.afterDeploy || !engine.config.contractsConfig.afterDeploy.length) {
process.exit();
}
engine.logger.info(__('Waiting for after deploy to finish...'));
engine.logger.info(__('You can exit with CTRL+C when after deploy completes'));
});
}
console(options) {
this.context = options.context || [constants.contexts.run, constants.contexts.console];
const REPL = require('./dashboard/repl.js');
const Engine = require('../lib/core/engine.js');
const engine = new Engine({
env: options.env,
client: options.client,
locale: options.locale,
version: this.version,
embarkConfig: options.embarkConfig || 'embark.json',
logFile: options.logFile,
logLevel: options.logLevel,
context: this.context,
webpackConfigName: options.webpackConfigName
});
async.waterfall([
function initEngine(callback) {
engine.init({}, callback);
},
function startServices(callback) {
let pluginList = engine.plugins.listPlugins();
if (pluginList.length > 0) {
engine.logger.info(__("loaded plugins") + ": " + pluginList.join(", "));
}
if (engine.ipc.connected) {
engine.startService("codeRunner");
engine.startService("console");
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) {
// Do specific work in case we are connected to a socket:
// - Setup Web3
// - Apply history
if(!engine.ipc.connected || engine.ipc.isServer()) {
return callback();
}
const Provider = require('../lib/modules/blockchain_connector/provider');
const Web3 = require('web3');
let web3 = new Web3();
engine.ipc.request("runcode:getCommands", null, (_, {web3Config, commands}) => {
const providerOptions = {
web3: web3,
accountsConfig: engine.config.contractsConfig.deployment.accounts,
blockchainConfig: engine.config.blockchainConfig,
logger: engine.logger,
isDev: engine.isDev,
type: engine.config.contractsConfig.deployment.type,
web3Endpoint: web3Config.providerUrl
};
const provider = new Provider(providerOptions);
web3.eth.defaultAccount = web3Config.defaultAccount;
provider.startWeb3Provider(() => {
engine.events.emit("runcode:register", "web3", web3);
async.each(commands, ({varName, code}, next) => {
if (varName) {
engine.events.emit("runcode:register", varName, code);
} else {
engine.events.request("runcode:eval", code);
}
next();
}, callback);
});
});
},
function deploy(callback) {
// Skip if we are connected to a websocket, the server will do it
if(engine.ipc.connected && engine.ipc.isClient()) {
return callback();
}
engine.config.reloadConfig();
engine.events.request('deploy:contracts', function (err) {
callback(err);
});
},
function waitForWriteFinish(callback) {
// Skip if we are connected to a websocket, the server will do it
if(engine.ipc.connected && engine.ipc.isClient()) {
return callback();
}
engine.logger.info("Finished deploying".underline);
engine.events.once('outputDone', (err) => {
engine.logger.info(__("finished building").underline);
callback(err);
});
},
function startREPL(callback) {
new REPL({events: engine.events, env: engine.env}).start(callback);
}
], function (err, _result) {
if (err) {
engine.logger.error(err.message);
engine.logger.info(err.stack);
} else {
engine.events.emit('firstDeploymentDone');
}
});
}
graph(options) {
this.context = options.context || [constants.contexts.graph];
options.onlyCompile = true;
const Engine = require('../lib/core/engine.js');
const engine = new Engine({
env: options.env,
version: this.version,
embarkConfig: options.embarkConfig || 'embark.json',
logFile: options.logFile,
context: this.context
});
async.waterfall([
function (callback) {
engine.init({}, callback);
},
function (callback) {
let pluginList = engine.plugins.listPlugins();
if (pluginList.length > 0) {
engine.logger.info(__("loaded plugins") + ": " + pluginList.join(", "));
}
engine.startService("processManager");
engine.startService("serviceMonitor");
engine.startService("libraryManager");
engine.startService("pipeline");
engine.startService("deployment", {onlyCompile: true});
engine.startService("web3");
engine.startService("codeGenerator");
engine.startService("graph");
engine.events.request('deploy:contracts', callback);
}
], (err) => {
if (err) {
engine.logger.error(err.message);
engine.logger.info(err.stack);
} else {
engine.events.request("graph:create", options, () => {
engine.logger.info(__("Done. %s generated", "./diagram.svg").underline);
});
}
process.exit();
});
}
reset() {
var fs = require('../lib/core/fs.js');
fs.removeSync('./chains.json');
fs.removeSync('.embark/');
fs.removeSync('node_modules/.cache');
fs.removeSync('dist/');
fs.removeSync('coverage/');
console.log(__("reset done!").green);
}
ejectWebpack() {
var fs = require('../lib/core/fs.js');
var dappConfig = fs.dappPath('webpack.config.js');
var embarkConfig = fs.embarkPath('lib/pipeline', 'webpack.config.js');
let ext = 1;
let dappConfigOld = dappConfig;
while (fs.existsSync(dappConfigOld)) {
dappConfigOld = dappConfig + `.${ext}`;
ext++;
}
if (dappConfigOld !== dappConfig) {
fs.copySync(dappConfig, dappConfigOld);
}
fs.copySync(embarkConfig, dappConfig);
console.log(__('webpack config ejected to: ').dim.yellow);
console.log(`${dappConfig}`.green);
}
upload(options) {
this.context = options.context || [constants.contexts.upload, constants.contexts.build];
const Engine = require('../lib/core/engine.js');
const engine = new Engine({
env: options.env,
client: options.client,
locale: options.locale,
version: this.version,
embarkConfig: 'embark.json',
interceptLogs: false,
logFile: options.logFile,
logLevel: options.logLevel,
events: options.events,
logger: options.logger,
config: options.config,
plugins: options.plugins,
context: this.context,
webpackConfigName: options.webpackConfigName
});
let platform;
async.waterfall([
function initEngine(callback) {
engine.init({}, () => {
if (engine.config.embarkConfig.config.storage === false || engine.config.storageConfig.enabled === false) {
engine.logger.error(__('Storage configuration is disabled in embark.json. Please provide a storage file before uploading'));
engine.logger.info(__('You can find an example here: %s', 'https://github.com/embark-framework/embark/blob/master/templates/demo/config/storage.js'.underline));
process.exit(1);
}
platform = engine.config.storageConfig.upload.provider;
callback();
});
},
function startServices(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");
callback();
},
function listLoadedPlugin(callback) {
let pluginList = engine.plugins.listPlugins();
if (pluginList.length > 0) {
engine.logger.info(__("loaded plugins") + ": " + pluginList.join(", "));
}
callback();
},
function deploy(callback) {
engine.events.on('outputDone', function () {
engine.events.request("storage:upload", callback);
});
engine.events.on('check:backOnline:Ethereum', function () {
engine.logger.info(__('Ethereum node detected') + '..');
engine.config.reloadConfig();
engine.events.request('deploy:contracts', function (err) {
if (err) {
return;
}
engine.logger.info(__('Deployment Done'));
});
});
},
function associateToENS(hash, callback) {
if(!options.ensDomain) {
return callback(null, hash);
}
engine.events.request("storage:ens:associate",
{name: options.ensDomain, storageHash: hash}, (err) => {
if (err) {
return callback(err);
}
engine.logger.info(__('ENS association completed for {{hash}} at {{domain}}', {hash, domain: options.ensDomain}));
callback();
});
}
], function (err) {
if (err) {
if (err.message) {
engine.logger.error(err.message);
return engine.logger.debug(err.stack);
}
engine.logger.error(err);
} else {
engine.logger.info((__("finished building DApp and deploying to") + " " + platform).underline);
}
// needed due to child processes
process.exit();
});
}
runTests(options) {
this.context = [constants.contexts.test];
let RunTests = require('../lib/tests/run_tests.js');
RunTests.run(options);
}
}
module.exports = EmbarkController;

View File

@ -0,0 +1,45 @@
let async = require('async');
let windowSize = require('window-size');
let Monitor = require('./monitor.js');
class Dashboard {
constructor(options) {
this.logger = options.logger;
this.events = options.events;
this.plugins = options.plugins;
this.version = options.version;
this.env = options.env;
this.events.on('firstDeploymentDone', this.checkWindowSize.bind(this));
this.events.on('outputDone', this.checkWindowSize.bind(this));
}
checkWindowSize() {
let size = windowSize.get();
if (size.height < 40 || size.width < 118) {
this.logger.warn(__("tip: you can resize the terminal or disable the dashboard with") + " embark run --nodashboard".bold.underline);
}
}
start(done) {
let monitor;
monitor = new Monitor({env: this.env, events: this.events});
this.logger.logFunction = monitor.logEntry;
this.events.on('contractsState', monitor.setContracts);
this.events.on('status', monitor.setStatus.bind(monitor));
this.events.on('servicesState', monitor.availableServices.bind(monitor));
this.events.setCommandHandler("console:command", monitor.executeCmd.bind(monitor));
this.logger.info('========================'.bold.green);
this.logger.info((__('Welcome to Embark') + ' ' + this.version).yellow.bold);
this.logger.info('========================'.bold.green);
done();
}
}
module.exports = Dashboard;

View File

@ -1,7 +1,7 @@
let blessed = require("neo-blessed");
let CommandHistory = require('./command_history.js');
class Dashboard {
class Monitor {
constructor(_options) {
let options = _options || {};
this.env = options.env;
@ -77,8 +77,8 @@ class Dashboard {
this.screen.render();
}
logEntry(text) {
this.logText.log(text);
logEntry() {
this.logText.log(...arguments);
this.screen.render();
}
@ -366,16 +366,18 @@ class Dashboard {
}
executeCmd(cmd, cb) {
const self = this;
self.logText.log('console> '.bold.green + cmd);
self.console.executeCmd(cmd, function (result) {
self.logText.log(result);
this.logText.log('console> '.bold.green + cmd);
this.events.request('console:executeCmd', cmd, (err, result) => {
let message = err || result;
if (message) {
this.logText.log(message);
}
if (cb) {
cb(result);
cb(message);
}
});
}
}
module.exports = Dashboard;
module.exports = Monitor;

43
cmd/dashboard/repl.js Normal file
View File

@ -0,0 +1,43 @@
const repl = require("repl");
const util = require("util");
class REPL {
constructor(options) {
this.events = options.events;
this.env = options.env;
}
enhancedEval(cmd, context, filename, callback) {
this.events.request('console:executeCmd', cmd.trim(), function (err, message) {
callback(err, message || ''); // This way, we don't print undefined
});
}
enhancedWriter(output) {
if ((typeof output) === "string") {
return output;
}
return util.inspect(output, {colors: true});
}
start(done) {
this.replServer = repl.start({
prompt: "Embark (" + this.env + ") > ",
useGlobal: true,
eval: this.enhancedEval.bind(this),
writer: this.enhancedWriter.bind(this)
});
this.events.request("runcode:getContext", (context) => {
this.replServer.context = context;
});
this.replServer.on("exit", () => {
process.exit();
});
done();
}
}
module.exports = REPL;

View File

@ -1,397 +0,0 @@
var EmbarkJS = {
onReady: function (cb) {
if (typeof (__embarkContext) === 'undefined') {
return cb();
}
return __embarkContext.execWhenReady(cb);
}
};
EmbarkJS.isNewWeb3 = function (web3Obj) {
var _web3 = web3Obj || (new Web3());
if (typeof(_web3.version) === "string") {
return true;
}
return parseInt(_web3.version.api.split('.')[0], 10) >= 1;
};
EmbarkJS.Contract = function (options) {
var self = this;
var i, abiElement;
var ContractClass;
this.abi = options.abi;
this.address = options.address;
this.gas = options.gas;
this.code = '0x' + options.code;
//this.web3 = options.web3 || web3;
this.web3 = options.web3;
if (!this.web3 && typeof (web3) !== 'undefined') {
this.web3 = web3;
} else if (!this.web3) {
this.web3 = window.web3;
}
if (EmbarkJS.isNewWeb3(this.web3)) {
ContractClass = new this.web3.eth.Contract(this.abi, this.address);
ContractClass.setProvider(this.web3.currentProvider);
ContractClass.options.data = this.code;
ContractClass.options.from = this.from || this.web3.eth.defaultAccount;
ContractClass.abi = ContractClass.options.abi;
ContractClass.address = this.address;
ContractClass.gas = this.gas;
let originalMethods = Object.keys(ContractClass);
ContractClass._jsonInterface.forEach((abi) => {
if (originalMethods.indexOf(abi.name) >= 0) {
console.log(abi.name + " is a reserved word and cannot be used as a contract method, property or event");
return;
}
if (!abi.inputs) {
return;
}
let numExpectedInputs = abi.inputs.length;
if (abi.type === 'function' && abi.constant) {
ContractClass[abi.name] = function () {
let options = {}, cb = null, args = Array.from(arguments || []).slice(0, numExpectedInputs);
if (typeof (arguments[numExpectedInputs]) === 'function') {
cb = arguments[numExpectedInputs];
} else if (typeof (arguments[numExpectedInputs]) === 'object') {
options = arguments[numExpectedInputs];
cb = arguments[numExpectedInputs + 1];
}
let ref = ContractClass.methods[abi.name];
let call = ref.apply(ref, ...arguments).call;
return call.apply(call, []);
};
} else if (abi.type === 'function') {
ContractClass[abi.name] = function () {
let options = {}, cb = null, args = Array.from(arguments || []).slice(0, numExpectedInputs);
if (typeof (arguments[numExpectedInputs]) === 'function') {
cb = arguments[numExpectedInputs];
} else if (typeof (arguments[numExpectedInputs]) === 'object') {
options = arguments[numExpectedInputs];
cb = arguments[numExpectedInputs + 1];
}
let ref = ContractClass.methods[abi.name];
let send = ref.apply(ref, args).send;
return send.apply(send, [options, cb]);
};
} else if (abi.type === 'event') {
ContractClass[abi.name] = function (options, cb) {
let ref = ContractClass.events[abi.name];
return ref.apply(ref, [options, cb]);
};
}
});
return ContractClass;
} else {
ContractClass = this.web3.eth.contract(this.abi);
this.eventList = [];
if (this.abi) {
for (i = 0; i < this.abi.length; i++) {
abiElement = this.abi[i];
if (abiElement.type === 'event') {
this.eventList.push(abiElement.name);
}
}
}
var messageEvents = function () {
this.cb = function () {
};
};
messageEvents.prototype.then = function (cb) {
this.cb = cb;
};
messageEvents.prototype.error = function (err) {
return err;
};
this._originalContractObject = ContractClass.at(this.address);
this._methods = Object.getOwnPropertyNames(this._originalContractObject).filter(function (p) {
// TODO: check for forbidden properties
if (self.eventList.indexOf(p) >= 0) {
self[p] = function () {
var promise = new messageEvents();
var args = Array.prototype.slice.call(arguments);
args.push(function (err, result) {
if (err) {
promise.error(err);
} else {
promise.cb(result);
}
});
self._originalContractObject[p].apply(self._originalContractObject[p], args);
return promise;
};
return true;
} else if (typeof self._originalContractObject[p] === 'function') {
self[p] = function (_args) {
var args = Array.prototype.slice.call(arguments);
var fn = self._originalContractObject[p];
var props = self.abi.find((x) => x.name == p);
var promise = new Promise(function (resolve, reject) {
args.push(function (err, transaction) {
promise.tx = transaction;
if (err) {
return reject(err);
}
var getConfirmation = function () {
self.web3.eth.getTransactionReceipt(transaction, function (err, receipt) {
if (err) {
return reject(err);
}
if (receipt !== null) {
return resolve(receipt);
}
setTimeout(getConfirmation, 1000);
});
};
if (typeof transaction !== "string" || props.constant) {
resolve(transaction);
} else {
getConfirmation();
}
});
fn.apply(fn, args);
});
return promise;
};
return true;
}
return false;
});
}
};
EmbarkJS.Contract.prototype.deploy = function (args, _options) {
var self = this;
var contractParams;
var options = _options || {};
contractParams = args || [];
contractParams.push({
from: this.web3.eth.accounts[0],
data: this.code,
gas: options.gas || 800000
});
var contractObject = this.web3.eth.contract(this.abi);
var promise = new Promise(function (resolve, reject) {
contractParams.push(function (err, transaction) {
if (err) {
reject(err);
} else if (transaction.address !== undefined) {
resolve(new EmbarkJS.Contract({
abi: self.abi,
code: self.code,
address: transaction.address
}));
}
});
// returns promise
// deploys contract
// wraps it around EmbarkJS.Contract
contractObject["new"].apply(contractObject, contractParams);
});
return promise;
};
EmbarkJS.Contract.prototype.new = EmbarkJS.Contract.prototype.deploy;
EmbarkJS.Contract.prototype.at = function (address) {
return new EmbarkJS.Contract({abi: this.abi, code: this.code, address: address});
};
EmbarkJS.Contract.prototype.send = function (value, unit, _options) {
var options, wei;
if (typeof unit === 'object') {
options = unit;
wei = value;
} else {
options = _options || {};
wei = this.web3.toWei(value, unit);
}
options.to = this.address;
options.value = wei;
this.web3.eth.sendTransaction(options);
};
EmbarkJS.Storage = {};
EmbarkJS.Storage.Providers = {};
EmbarkJS.Storage.saveText = function (text) {
if (!this.currentStorage) {
throw new Error('Storage provider not set; e.g EmbarkJS.Storage.setProvider("ipfs")');
}
return this.currentStorage.saveText(text);
};
EmbarkJS.Storage.get = function (hash) {
if (!this.currentStorage) {
throw new Error('Storage provider not set; e.g EmbarkJS.Storage.setProvider("ipfs")');
}
return this.currentStorage.get(hash);
};
EmbarkJS.Storage.uploadFile = function (inputSelector) {
if (!this.currentStorage) {
throw new Error('Storage provider not set; e.g EmbarkJS.Storage.setProvider("ipfs")');
}
return this.currentStorage.uploadFile(inputSelector);
};
EmbarkJS.Storage.getUrl = function (hash) {
if (!this.currentStorage) {
throw new Error('Storage provider not set; e.g EmbarkJS.Storage.setProvider("ipfs")');
}
return this.currentStorage.getUrl(hash);
};
EmbarkJS.Storage.registerProvider = function (providerName, obj) {
EmbarkJS.Storage.Providers[providerName] = obj;
};
EmbarkJS.Storage.setProvider = function (provider, options) {
let providerObj = this.Providers[provider];
if (!providerObj) {
throw new Error('Unknown storage provider');
}
this.currentStorage = providerObj;
return providerObj.setProvider(options);
};
EmbarkJS.Storage.isAvailable = function () {
if (!this.currentStorage) {
throw new Error('Storage provider not set; e.g EmbarkJS.Storage.setProvider("ipfs")');
}
return this.currentStorage.isAvailable();
};
EmbarkJS.Messages = {};
EmbarkJS.Messages.Providers = {};
EmbarkJS.Messages.registerProvider = function (providerName, obj) {
EmbarkJS.Messages.Providers[providerName] = obj;
};
EmbarkJS.Messages.setProvider = function (provider, options) {
let providerObj = this.Providers[provider];
if (!providerObj) {
throw new Error('Unknown messages provider');
}
this.currentMessages = providerObj;
return providerObj.setProvider(options);
};
EmbarkJS.Messages.isAvailable = function () {
return this.currentMessages.isAvailable();
};
EmbarkJS.Messages.sendMessage = function (options) {
if (!this.currentMessages) {
throw new Error('Messages provider not set; e.g EmbarkJS.Messages.setProvider("whisper")');
}
return this.currentMessages.sendMessage(options);
};
EmbarkJS.Messages.listenTo = function (options, callback) {
if (!this.currentMessages) {
throw new Error('Messages provider not set; e.g EmbarkJS.Messages.setProvider("whisper")');
}
return this.currentMessages.listenTo(options, callback);
};
EmbarkJS.Names = {};
EmbarkJS.Names.Providers = {};
EmbarkJS.Names.registerProvider = function (providerName, obj) {
EmbarkJS.Names.Providers[providerName] = obj;
};
EmbarkJS.Names.setProvider = function (provider, options) {
let providerObj = this.Providers[provider];
if (!providerObj) {
throw new Error('Unknown name system provider');
}
this.currentNameSystems = providerObj;
return providerObj.setProvider(options);
};
// resolve resolves a name into an identifier of some kind
EmbarkJS.Names.resolve = function (name) {
if (!this.currentNameSystems) {
throw new Error('Name system provider not set; e.g EmbarkJS.Names.setProvider("ens")');
}
return this.currentNameSystems.resolve(name);
};
// the reverse of resolve, resolves using an identifier to get to a name
EmbarkJS.Names.lookup = function (identifier) {
if (!this.currentNameSystems) {
throw new Error('Name system provider not set; e.g EmbarkJS.Names.setProvider("ens")');
}
return this.currentNameSystems.lookup(identifier);
};
// To Implement
/*
// register a name
EmbarkJS.Names.register = function(name, options) {
}
*/
EmbarkJS.Utils = {
fromAscii: function (str) {
var _web3 = new Web3();
return _web3.utils ? _web3.utils.fromAscii(str) : _web3.fromAscii(str);
},
toAscii: function (str) {
var _web3 = new Web3();
return _web3.utils.toAscii(str);
}
};
export default EmbarkJS;

View File

@ -1,396 +0,0 @@
var EmbarkJS = {
onReady: function(cb) {
if (typeof (__embarkContext) === 'undefined') {
return cb();
}
return __embarkContext.execWhenReady(cb);
}
};
EmbarkJS.isNewWeb3 = function(web3Obj) {
var _web3 = web3Obj || (new Web3());
if (typeof(_web3.version) === "string") {
return true;
}
return parseInt(_web3.version.api.split('.')[0], 10) >= 1;
};
EmbarkJS.Contract = function(options) {
var self = this;
var i, abiElement;
var ContractClass;
this.abi = options.abi;
this.address = options.address;
this.gas = options.gas;
this.code = '0x' + options.code;
//this.web3 = options.web3 || web3;
this.web3 = options.web3;
if (!this.web3 && typeof (web3) !== 'undefined') {
this.web3 = web3;
} else if (!this.web3) {
this.web3 = window.web3;
}
if (EmbarkJS.isNewWeb3(this.web3)) {
ContractClass = new this.web3.eth.Contract(this.abi, this.address);
ContractClass.setProvider(this.web3.currentProvider);
ContractClass.options.data = this.code;
ContractClass.options.from = this.from || this.web3.eth.defaultAccount;
ContractClass.abi = ContractClass.options.abi;
ContractClass.address = this.address;
ContractClass.gas = this.gas;
let originalMethods = Object.keys(ContractClass);
ContractClass._jsonInterface.forEach((abi) => {
if (originalMethods.indexOf(abi.name) >= 0) {
console.log(abi.name + " is a reserved word and cannot be used as a contract method, property or event");
return;
}
if (!abi.inputs) {
return;
}
let numExpectedInputs = abi.inputs.length;
if (abi.type === 'function' && abi.constant) {
ContractClass[abi.name] = function() {
let options = {}, cb = null, args = Array.from(arguments || []).slice(0, numExpectedInputs);
if (typeof (arguments[numExpectedInputs]) === 'function') {
cb = arguments[numExpectedInputs];
} else if (typeof (arguments[numExpectedInputs]) === 'object') {
options = arguments[numExpectedInputs];
cb = arguments[numExpectedInputs + 1];
}
let ref = ContractClass.methods[abi.name];
let call = ref.apply(ref, ...arguments).call;
return call.apply(call, []);
};
} else if (abi.type === 'function') {
ContractClass[abi.name] = function() {
let options = {}, cb = null, args = Array.from(arguments || []).slice(0, numExpectedInputs);
if (typeof (arguments[numExpectedInputs]) === 'function') {
cb = arguments[numExpectedInputs];
} else if (typeof (arguments[numExpectedInputs]) === 'object') {
options = arguments[numExpectedInputs];
cb = arguments[numExpectedInputs + 1];
}
let ref = ContractClass.methods[abi.name];
let send = ref.apply(ref, args).send;
return send.apply(send, [options, cb]);
};
} else if (abi.type === 'event') {
ContractClass[abi.name] = function(options, cb) {
let ref = ContractClass.events[abi.name];
return ref.apply(ref, [options, cb]);
}
}
});
return ContractClass;
} else {
ContractClass = this.web3.eth.contract(this.abi);
this.eventList = [];
if (this.abi) {
for (i = 0; i < this.abi.length; i++) {
abiElement = this.abi[i];
if (abiElement.type === 'event') {
this.eventList.push(abiElement.name);
}
}
}
var messageEvents = function() {
this.cb = function() {};
};
messageEvents.prototype.then = function(cb) {
this.cb = cb;
};
messageEvents.prototype.error = function(err) {
return err;
};
this._originalContractObject = ContractClass.at(this.address);
this._methods = Object.getOwnPropertyNames(this._originalContractObject).filter(function(p) {
// TODO: check for forbidden properties
if (self.eventList.indexOf(p) >= 0) {
self[p] = function() {
var promise = new messageEvents();
var args = Array.prototype.slice.call(arguments);
args.push(function(err, result) {
if (err) {
promise.error(err);
} else {
promise.cb(result);
}
});
self._originalContractObject[p].apply(self._originalContractObject[p], args);
return promise;
};
return true;
} else if (typeof self._originalContractObject[p] === 'function') {
self[p] = function(_args) {
var args = Array.prototype.slice.call(arguments);
var fn = self._originalContractObject[p];
var props = self.abi.find((x) => x.name == p);
var promise = new Promise(function(resolve, reject) {
args.push(function(err, transaction) {
promise.tx = transaction;
if (err) {
return reject(err);
}
var getConfirmation = function() {
self.web3.eth.getTransactionReceipt(transaction, function(err, receipt) {
if (err) {
return reject(err);
}
if (receipt !== null) {
return resolve(receipt);
}
setTimeout(getConfirmation, 1000);
});
};
if (typeof(transaction) !== "string" || props.constant) {
resolve(transaction);
} else {
getConfirmation();
}
});
fn.apply(fn, args);
});
return promise;
};
return true;
}
return false;
});
}
};
EmbarkJS.Contract.prototype.deploy = function(args, _options) {
var self = this;
var contractParams;
var options = _options || {};
contractParams = args || [];
contractParams.push({
from: this.web3.eth.accounts[0],
data: this.code,
gas: options.gas || 800000
});
var contractObject = this.web3.eth.contract(this.abi);
var promise = new Promise(function(resolve, reject) {
contractParams.push(function(err, transaction) {
if (err) {
reject(err);
} else if (transaction.address !== undefined) {
resolve(new EmbarkJS.Contract({
abi: self.abi,
code: self.code,
address: transaction.address
}));
}
});
// returns promise
// deploys contract
// wraps it around EmbarkJS.Contract
contractObject["new"].apply(contractObject, contractParams);
});
return promise;
};
EmbarkJS.Contract.prototype.new = EmbarkJS.Contract.prototype.deploy;
EmbarkJS.Contract.prototype.at = function(address) {
return new EmbarkJS.Contract({ abi: this.abi, code: this.code, address: address });
};
EmbarkJS.Contract.prototype.send = function(value, unit, _options) {
var options, wei;
if (typeof unit === 'object') {
options = unit;
wei = value;
} else {
options = _options || {};
wei = this.web3.toWei(value, unit);
}
options.to = this.address;
options.value = wei;
this.web3.eth.sendTransaction(options);
};
EmbarkJS.Storage = {};
EmbarkJS.Storage.Providers = {};
EmbarkJS.Storage.saveText = function(text) {
if (!this.currentStorage) {
throw new Error('Storage provider not set; e.g EmbarkJS.Storage.setProvider("ipfs")');
}
return this.currentStorage.saveText(text);
};
EmbarkJS.Storage.get = function(hash) {
if (!this.currentStorage) {
throw new Error('Storage provider not set; e.g EmbarkJS.Storage.setProvider("ipfs")');
}
return this.currentStorage.get(hash);
};
EmbarkJS.Storage.uploadFile = function(inputSelector) {
if (!this.currentStorage) {
throw new Error('Storage provider not set; e.g EmbarkJS.Storage.setProvider("ipfs")');
}
return this.currentStorage.uploadFile(inputSelector);
};
EmbarkJS.Storage.getUrl = function(hash) {
if (!this.currentStorage) {
throw new Error('Storage provider not set; e.g EmbarkJS.Storage.setProvider("ipfs")');
}
return this.currentStorage.getUrl(hash);
};
EmbarkJS.Storage.registerProvider = function(providerName, obj) {
EmbarkJS.Storage.Providers[providerName] = obj;
};
EmbarkJS.Storage.setProvider = function(provider, options) {
let providerObj = this.Providers[provider];
if (!providerObj) {
throw new Error('Unknown storage provider');
}
this.currentStorage = providerObj;
return providerObj.setProvider(options);
};
EmbarkJS.Storage.isAvailable = function(){
if (!this.currentStorage) {
throw new Error('Storage provider not set; e.g EmbarkJS.Storage.setProvider("ipfs")');
}
return this.currentStorage.isAvailable();
};
EmbarkJS.Messages = {};
EmbarkJS.Messages.Providers = {};
EmbarkJS.Messages.registerProvider = function(providerName, obj) {
EmbarkJS.Messages.Providers[providerName] = obj;
};
EmbarkJS.Messages.setProvider = function(provider, options) {
let providerObj = this.Providers[provider];
if (!providerObj) {
throw new Error('Unknown messages provider');
}
this.currentMessages = providerObj;
return providerObj.setProvider(options);
};
EmbarkJS.Messages.isAvailable = function(){
return this.currentMessages.isAvailable();
};
EmbarkJS.Messages.sendMessage = function(options) {
if (!this.currentMessages) {
throw new Error('Messages provider not set; e.g EmbarkJS.Messages.setProvider("whisper")');
}
return this.currentMessages.sendMessage(options);
};
EmbarkJS.Messages.listenTo = function(options, callback) {
if (!this.currentMessages) {
throw new Error('Messages provider not set; e.g EmbarkJS.Messages.setProvider("whisper")');
}
return this.currentMessages.listenTo(options, callback);
};
EmbarkJS.Names = {};
EmbarkJS.Names.Providers = {};
EmbarkJS.Names.registerProvider = function(providerName, obj) {
EmbarkJS.Names.Providers[providerName] = obj;
}
EmbarkJS.Names.setProvider = function(provider, options) {
let providerObj = this.Providers[provider];
if (!providerObj) {
throw new Error('Unknown name system provider');
}
this.currentNameSystems = providerObj;
return providerObj.setProvider(options);
};
// resolve resolves a name into an identifier of some kind
EmbarkJS.Names.resolve = function(name) {
if (!this.currentNameSystems) {
throw new Error('Name system provider not set; e.g EmbarkJS.Names.setProvider("ens")');
}
return this.currentNameSystems.resolve(name);
}
// the reverse of resolve, resolves using an identifier to get to a name
EmbarkJS.Names.lookup = function(identifier) {
if (!this.currentNameSystems) {
throw new Error('Name system provider not set; e.g EmbarkJS.Names.setProvider("ens")');
}
return this.currentNameSystems.lookup(identifier);
}
// To Implement
/*
// register a name
EmbarkJS.Names.register = function(name, options) {
}
*/
EmbarkJS.Utils = {
fromAscii: function(str) {
var _web3 = new Web3();
return _web3.utils ? _web3.utils.fromAscii(str) : _web3.fromAscii(str);
},
toAscii: function(str) {
var _web3 = new Web3();
return _web3.utils.toAscii(str);
}
};
module.exports = EmbarkJS;

View File

@ -1,149 +0,0 @@
// Adapted from Iuri Matias' Embark framework
// https://github.com/iurimatias/embark-framework
// Modified by ryepdx to mine at regular intervals.
(function() {
var main = function () {
/* TODO: Find a way to load mining config from YML.
if (!loadScript("config.js")) {
console.log("== config.js not found");
}
if (typeof(config) === "undefined") {
config = {};
console.log("== config is undefined, proceeding with defaults");
}
In the meantime, just set an empty config object.
*/
config = {};
defaults = {
interval_ms: 15000,
initial_ether: 15000000000000000000,
mine_pending_txns: true,
mine_periodically: false,
mine_normally: false,
threads: 1
};
for (var key in defaults) {
if (config[key] === undefined) {
config[key] = defaults[key];
}
}
var miner_obj = (admin.miner === undefined) ? miner : admin.miner;
if (config.mine_normally) {
//miner_obj.start(config.threads);
miner_obj.start();
return;
}
// TODO: check why it's no longer accepting this param
//miner_obj.stop(config.threads);
miner_obj.stop();
fundAccount(config, miner_obj, function () {
if (config.mine_periodically) start_periodic_mining(config, miner_obj);
if (config.mine_pending_txns) start_transaction_mining(config, miner_obj);
});
};
var fundAccount = function (config, miner_obj, cb) {
var accountFunded = function () {
return (eth.getBalance(eth.coinbase) >= config.initial_ether);
};
if (accountFunded()) {
return cb();
}
console.log("== Funding account");
miner_obj.start();
var blockWatcher = web3.eth.filter("latest").watch(function () {
if (accountFunded()) {
console.log("== Account funded");
blockWatcher.stopWatching();
//miner_obj.stop(config.threads);
miner_obj.stop();
cb();
}
});
};
var pendingTransactions = function() {
if (web3.eth.pendingTransactions === undefined || web3.eth.pendingTransactions === null) {
return txpool.status.pending || txpool.status.queued;
}
else if (typeof web3.eth.pendingTransactions === "function") {
return web3.eth.pendingTransactions().length > 0;
}
else {
return web3.eth.pendingTransactions.length > 0 || web3.eth.getBlock('pending').transactions.length > 0;
}
};
var start_periodic_mining = function (config, miner_obj) {
var last_mined_ms = Date.now();
var timeout_set = false;
//miner_obj.start(config.threads);
miner_obj.start();
web3.eth.filter("latest").watch(function () {
if ((config.mine_pending_txns && pendingTransactions()) || timeout_set) {
return;
}
timeout_set = true;
var now = Date.now();
var ms_since_block = now - last_mined_ms;
last_mined_ms = now;
var next_block_in_ms;
if (ms_since_block > config.interval_ms) {
next_block_in_ms = 0;
} else {
next_block_in_ms = (config.interval_ms - ms_since_block);
}
//miner_obj.stop(config.threads);
miner_obj.stop();
console.log("== Looking for next block in " + next_block_in_ms + "ms");
setTimeout(function () {
console.log("== Looking for next block");
timeout_set = false;
//miner_obj.start(config.threads);
miner_obj.start();
}, next_block_in_ms);
});
};
var start_transaction_mining = function (config, miner_obj) {
web3.eth.filter("pending").watch(function () {
if (miner_obj.hashrate > 0) return;
console.log("== Pending transactions! Looking for next block...");
//miner_obj.start(config.threads);
miner_obj.start();
});
if (config.mine_periodically) return;
web3.eth.filter("latest").watch(function () {
if (!pendingTransactions()) {
console.log("== No transactions left. Stopping miner...");
//miner_obj.stop(config.threads);
miner_obj.stop();
}
});
};
main();
})();

1
js/web3-1.0.min.js vendored

File diff suppressed because one or more lines are too long

View File

@ -1,118 +0,0 @@
const Viz = require('viz.js');
const fs = require('fs');
class GraphGenerator {
constructor(engine) {
this.engine = engine;
}
generate(options) {
let id = 0;
let contractString = "";
let relationshipString = "";
let idMapping = {};
let contractInheritance = {};
for (let contract in this.engine.contractsManager.contracts) {
if(options.skipUndeployed && !this.engine.contractsManager.contracts[contract].deploy) continue;
id++;
idMapping[contract] = id;
let contractLabel = "";
contractLabel += `${contract}`;
let tooltip = contract;
if(this.engine.contractsManager.contracts[contract].instanceOf !== undefined &&
this.engine.contractsManager.contracts[this.engine.contractsManager.contracts[contract].instanceOf] !== undefined){
contractInheritance[contract] = this.engine.contractsManager.contracts[contract].instanceOf;
contractLabel += ": " + this.engine.contractsManager.contracts[contract].instanceOf;
tooltip += " instance of " + this.engine.contractsManager.contracts[contract].instanceOf;
} else {
if(!(options.skipFunctions === true && options.skipEvents === true)) contractLabel += "|";
for(let i = 0; i < this.engine.contractsManager.contracts[contract].abiDefinition.length; i++){
let abiDef = this.engine.contractsManager.contracts[contract].abiDefinition[i];
if(abiDef.type == 'event' && options.skipEvents) continue;
if(['constructor', 'fallback'].indexOf(abiDef.type) > -1 && options.skipFunctions) continue;
switch(abiDef.type){
case 'fallback':
contractLabel += "«fallback»()\\l";
break;
case 'constructor':
contractLabel += "«constructor»(";
abiDef.inputs.forEach(function(elem, index){
contractLabel += (index == 0 ? "" : ", ") + elem.type;
});
contractLabel += ")\\l";
break;
case 'event':
contractLabel += "«event»" + abiDef.name + "(";
abiDef.inputs.forEach(function(elem, index){
contractLabel += (index == 0 ? "" : ", ") + elem.type;
});
contractLabel += ")\\l";
break;
default: break;
}
}
let fHashes = this.engine.contractsManager.contracts[contract].functionHashes;
if(fHashes != {} && fHashes != undefined && !options.skipFunctions){
for(let method in this.engine.contractsManager.contracts[contract].functionHashes){
contractLabel += method + '\\l';
}
}
}
let others = '';
if(!this.engine.contractsManager.contracts[contract].deploy){
others = 'fontcolor="#c3c3c3", color="#a0a0a0"';
tooltip += " (not deployed)";
}
contractString += `${id}[label = "{${contractLabel}}", tooltip="${tooltip}", fillcolor=gray95, ${others}]\n`;
}
for (let c in this.engine.contractsManager.contractDependencies){
let contractDependencies = Array.from(new Set(this.engine.contractsManager.contractDependencies[c]));
contractDependencies.forEach((d) => {
if(idMapping[c] !== undefined && idMapping[d] !== undefined){
if(options.skipUndeployed && this.engine.contractsManager.contracts[c].deploy && this.engine.contractsManager.contracts[d].deploy){
relationshipString += `${idMapping[d]}->${idMapping[c]}[constraint=true, arrowtail=diamond, tooltip="${c} uses ${d}"]\n`;
}
}
});
}
for (let c in contractInheritance){
if(options.skipUndeployed && !this.engine.contractsManager.contracts[contractInheritance[c]].deploy) continue;
relationshipString += `${idMapping[contractInheritance[c]]}->${idMapping[c]}[tooltip="${c} instance of ${contractInheritance[c]}"]\n`;
}
let dot = `
digraph Contracts {
node[shape=record,style=filled]
edge[dir=back, arrowtail=empty]
${contractString}
${relationshipString}
}`;
let svg = Viz(dot);
let filename = "diagram.svg";
fs.writeFileSync(filename, svg, (err) => {
if (err) throw err;
});
}
}
module.exports = GraphGenerator;

View File

@ -1,8 +0,0 @@
var fs = require('../core/fs.js');
module.exports = function() {
fs.removeSync('./chains.json');
fs.removeSync('.embark/');
fs.removeSync('dist/');
console.log(__("reset done!").green);
};

View File

@ -1,60 +0,0 @@
let shelljs = require('shelljs');
let proxy = require('../core/proxy');
const Ipc = require('../core/ipc');
const constants = require('../constants.json');
const {defaultHost, dockerHostSwap} = require('../utils/host');
class Simulator {
constructor(options) {
this.blockchainConfig = options.blockchainConfig;
this.logger = options.logger;
}
run(options) {
let cmds = [];
const testrpc = shelljs.which('testrpc');
const ganache = shelljs.which('ganache-cli');
if (!testrpc && !ganache) {
this.logger.warn(__('%s is not installed on your machine', 'Ganache CLI (TestRPC)'));
this.logger.info(__('You can install it by running: %s', 'npm -g install ganache-cli'));
process.exit();
}
let useProxy = this.blockchainConfig.proxy || false;
let host = (dockerHostSwap(options.host || this.blockchainConfig.rpcHost) || defaultHost);
let port = (options.port || this.blockchainConfig.rpcPort || 8545);
cmds.push("-p " + (port + (useProxy ? constants.blockchain.servicePortOnProxy : 0)));
if (!ganache) {
cmds.push("-h " + host);
}
cmds.push("-a " + (options.numAccounts || 10));
cmds.push("-e " + (options.defaultBalance || 100));
cmds.push("-l " + (options.gasLimit || 8000000));
// adding mnemonic only if it is defined in the blockchainConfig or options
let simulatorMnemonic = this.blockchainConfig.simulatorMnemonic || options.simulatorMnemonic;
if (simulatorMnemonic) {
cmds.push("--mnemonic \"" + (simulatorMnemonic) +"\"");
}
// adding blocktime only if it is defined in the blockchainConfig or options
let simulatorBlocktime = this.blockchainConfig.simulatorBlocktime || options.simulatorBlocktime;
if (simulatorBlocktime) {
cmds.push("-b \"" + (simulatorBlocktime) +"\"");
}
const program = ganache ? 'ganache-cli' : 'testrpc';
console.log(`running: ${program} ${cmds.join(' ')}`);
shelljs.exec(`${program} ${cmds.join(' ')}`, {async : true});
if(useProxy){
let ipcObject = new Ipc({ipcRole: 'client'});
proxy.serve(ipcObject, host, port, false);
}
}
}
module.exports = Simulator;

View File

@ -1,39 +0,0 @@
let fs = require('../core/fs.js');
let utils = require('../utils/utils.js');
class TemplateGenerator {
constructor(templateName) {
this.templateName = templateName;
}
generate(destinationFolder, name) {
let templatePath = fs.embarkPath(utils.joinPath('templates', this.templateName));
console.log(__('Initializing Embark Template....').green);
let fspath = utils.joinPath(destinationFolder, name);
fs.copySync(templatePath, fspath);
utils.cd(fspath);
utils.sed('package.json', '%APP_NAME%', name);
if (fs.existsSync('dot.gitignore')) {
fs.moveSync('dot.gitignore', '.gitignore');
}
if (name === 'embark_demo') {
console.log(__('Installing packages...').green);
utils.runCmd('npm install');
}
console.log(__('Init complete').green);
console.log('\n' + __('App ready at ').green + fspath);
if (name === 'embark_demo') {
console.log('-------------------'.yellow);
console.log(__('Next steps:').green);
console.log(('-> ' + ('cd ' + fspath).bold.cyan).green);
console.log('-> '.green + 'embark run'.bold.cyan);
console.log(__('For more info go to http://embark.status.im').green);
}
}
}
module.exports = TemplateGenerator;

View File

@ -1,32 +0,0 @@
// still needs to be run on a separate file due to the global context
var RunCode = require('./runCode.js');
class CodeRunner {
constructor(options) {
this.plugins = options.plugins;
this.logger = options.logger;
this.events = options.events;
// necessary to init the context
RunCode.initContext();
this.events.on("runcode:register", (varName, code) => {
RunCode.registerVar(varName, code);
});
this.events.setCommandHandler('runcode:eval', (code, cb) => {
if (!cb) {
cb = function() {};
}
try {
let result = RunCode.doEval(code);
cb(null, result);
} catch (e) {
cb(e);
}
});
}
}
module.exports = CodeRunner;

View File

@ -1,30 +0,0 @@
/*eslint no-unused-vars: off*/
let __mainContext = this;
function initContext() {
doEval("__mainContext = this");
}
// ======================
// the eval is used for evaluating some of the contact calls for different purposes
// this should be at least moved to a different process and scope
// for now it is defined here
// ======================
function doEval(code) {
try {
// TODO: add trace log here
return eval(code);
} catch(e) {
throw new Error(e + "\n" + code);
}
}
function registerVar(varName, code) {
__mainContext[varName] = code;
}
module.exports = {
doEval: doEval,
registerVar: registerVar,
initContext: initContext
};

View File

@ -7,15 +7,12 @@
"run": "run",
"upload": "upload",
"build": "build",
"console": "console",
"graph": "graph",
"test": "test",
"reset": "reset",
"any": "any"
},
"events": {
"contractFilesChanged": "contractFilesChanged",
"contractConfigChanged": "contractConfigChanged"
},
"process": {
"processLaunchRequest": "process:launch-request",
"processLaunchComplete": "process:launch-complete",
@ -34,9 +31,16 @@
},
"blockchain": {
"blockchainReady": "blockchainReady",
"blockchainExit": "blockchainExit",
"init": "init",
"initiated": "initiated",
"servicePortOnProxy": 10
"servicePortOnProxy": 10,
"networkIds": {
"livenet": 1,
"testnet": 3,
"ropsten": 3,
"rinkeby": 4
}
},
"storage": {
"init": "init",

View File

@ -1 +0,0 @@
__mainContext.<%- className %> = new EmbarkJS.Contract({abi: <%- abi %>, address: <%- contractAddress %>, code: '<%- contract.code %>', gasEstimates: <%- gasEstimates %>});

View File

@ -1 +0,0 @@
var __mainContext = __mainContext || this;

View File

@ -1,55 +0,0 @@
function __reduce(arr, memo, iteratee, cb) {
if (typeof cb !== 'function') {
if (typeof memo === 'function' && typeof iteratee === 'function') {
cb = iteratee;
iteratee = memo;
memo = [];
} else {
throw new TypeError('expected callback to be a function');
}
}
if (!Array.isArray(arr)) {
cb(new TypeError('expected an array'));
return;
}
if (typeof iteratee !== 'function') {
cb(new TypeError('expected iteratee to be a function'));
return;
}
(function next(i, acc) {
if (i === arr.length) {
cb(null, acc);
return;
}
iteratee(acc, arr[i], function(err, val) {
if (err) {
cb(err);
return;
}
next(i + 1, val);
});
})(0, memo);
};
function __isNewWeb3_1() {
return (typeof(web3.version) === "string");
};
function __getAccounts(cb) {
if (__isNewWeb3_1()) {
web3.eth.getAccounts().then(function(accounts) {
cb(null, accounts);
return null;
}).catch(function(err) {
cb(err);
return null;
});
return;
}
web3.eth.getAccounts(cb);
};

View File

@ -1,38 +0,0 @@
__mainContext.web3 = undefined;
__reduce(<%- connectionList %>,function(prev, value, next) {
if (prev === false) {
return next(null, false);
}
if (value === '$WEB3' && (typeof web3 !== 'undefined' && typeof Web3 !== 'undefined')) {
web3.setProvider(web3.givenProvider);
} else if (value !== '$WEB3' && (typeof Web3 !== 'undefined' && ((typeof web3 === 'undefined') || (typeof web3 !== 'undefined' && (!web3.isConnected || (web3.isConnected && !web3.isConnected())))))) {
if (value.indexOf('ws://') >= 0) {
web3.setProvider(new Web3.providers.WebsocketProvider(value));
} else {
web3.setProvider(new Web3.providers.HttpProvider(value));
}
} else if (value === '$WEB3') {
return next(null, '');
}
__getAccounts(function(err, account) {
if (err) {
next(null, true)
} else {
next(null, false)
}
});
}, function(err, _result) {
__getAccounts(function(err, accounts) {
<% if (warnAboutMetamask) { %>
if (web3.eth.currentProvider && web3.eth.currentProvider.isMetaMask) {
console.log("%cNote: Embark has detected you are in the development environment and using Metamask, please make sure Metamask is connected to your local node", "font-size: 2em");
}
<% } %>
if (accounts) {
web3.eth.defaultAccount = accounts[0];
}
<%- done %>
});
});

View File

@ -1,118 +0,0 @@
let async = require('async');
class DeployManager {
constructor(options) {
const self = this;
this.config = options.config;
this.logger = options.logger;
this.blockchainConfig = this.config.blockchainConfig;
this.events = options.events;
this.plugins = options.plugins;
this.blockchain = options.blockchain;
this.gasLimit = false;
this.fatalErrors = false;
this.deployOnlyOnConfig = false;
this.onlyCompile = options.onlyCompile !== undefined ? options.onlyCompile : false;
this.events.setCommandHandler('deploy:contracts', (cb) => {
self.deployContracts(cb);
});
}
deployAll(done) {
let self = this;
self.events.request('contracts:list', (err, contracts) => {
if (err) {
return done(err);
}
self.logger.info(__("deploying contracts"));
self.events.emit("deploy:beforeAll");
async.eachOfSeries(contracts,
function (contract, key, callback) {
contract._gasLimit = self.gasLimit;
self.events.request('deploy:contract', contract, (err) => {
callback(err);
});
},
function (err, _results) {
if (err) {
self.logger.error(__("error deploying contracts"));
self.logger.error(err.message);
self.logger.debug(err.stack);
}
if (contracts.length === 0) {
self.logger.info(__("no contracts found"));
return done();
}
self.logger.info(__("finished deploying contracts"));
done(err);
}
);
});
}
deployContracts(done) {
let self = this;
if (self.blockchainConfig === {} || self.blockchainConfig.enabled === false) {
self.logger.info(__("Blockchain component is disabled in the config").underline);
this.events.emit('blockchainDisabled', {});
return done();
}
async.waterfall([
function buildContracts(callback) {
self.events.request("contracts:build", self.deployOnlyOnConfig, (err) => {
callback(err);
});
},
// TODO: shouldn't be necessary
function checkCompileOnly(callback){
if(self.onlyCompile){
self.events.emit('contractsDeployed');
return done();
}
return callback();
},
// TODO: could be implemented as an event (beforeDeployAll)
function checkIsConnectedToBlockchain(callback) {
self.blockchain.onReady(() => {
self.blockchain.assertNodeConnection(callback);
});
},
// TODO: this can be done on the fly or as part of the initialization
function determineDefaultAccount(callback) {
self.blockchain.determineDefaultAccount((err) => {
callback(err);
});
},
function deployAllContracts(callback) {
self.deployAll(function (err) {
if (!err) {
self.events.emit('contractsDeployed');
}
if (err && self.fatalErrors) {
return callback(err);
}
callback();
});
},
function runAfterDeploy(callback) {
self.plugins.emitAndRunActionsForEvent('contracts:deploy:afterAll', callback);
}
], function (err, _result) {
done(err);
});
}
}
module.exports = DeployManager;

View File

@ -1,113 +0,0 @@
const ProviderEngine = require('embark-web3-provider-engine');
const RpcSubprovider = require('embark-web3-provider-engine/subproviders/rpc');
const WsSubprovider = require('embark-web3-provider-engine/subproviders/websocket');
const async = require('async');
const AccountParser = require('./accountParser');
const fundAccount = require('./fundAccount');
const NO_ACCOUNTS = 'noAccounts';
class Provider {
constructor(options) {
this.web3 = options.web3;
this.accountsConfig = options.accountsConfig;
this.blockchainConfig = options.blockchainConfig;
this.type = options.type;
this.web3Endpoint = options.web3Endpoint;
this.logger = options.logger;
this.isDev = options.isDev;
this.engine = new ProviderEngine();
this.asyncMethods = {};
}
startWeb3Provider(callback) {
const self = this;
if (this.type === 'rpc') {
self.engine.addProvider(new RpcSubprovider({
rpcUrl: self.web3Endpoint
}));
} else if (this.type === 'ws') {
self.engine.addProvider(new WsSubprovider({
rpcUrl: self.web3Endpoint,
origin: this.blockchainConfig.wsOrigins.split(',')[0]
}));
} else {
return callback(__("contracts config error: unknown deployment type %s", this.type));
}
// network connectivity error
self.engine.on('error', (err) => {
// report connectivity errors
self.logger.error(err.stack);
});
self.engine.start();
self.web3.setProvider(self);
self.accounts = AccountParser.parseAccountsConfig(self.accountsConfig, self.web3, self.logger);
self.addresses = [];
async.waterfall([
function populateWeb3Wallet(next) {
if (!self.accounts.length) {
return next(NO_ACCOUNTS);
}
self.accounts.forEach(account => {
self.addresses.push(account.address);
self.web3.eth.accounts.wallet.add(account);
});
self.asyncMethods = {
eth_accounts: self.eth_accounts.bind(self)
};
next();
}
], function (err) {
if (err && err !== NO_ACCOUNTS) {
self.logger.error((err));
}
callback();
});
}
fundAccounts(callback) {
const self = this;
if (!self.accounts.length) {
return callback();
}
if (!self.isDev) {
return callback();
}
async.eachLimit(self.accounts, 1, (account, eachCb) => {
fundAccount(self.web3, account.address, account.hexBalance, eachCb);
}, callback);
}
stop() {
this.engine.stop();
}
eth_accounts(payload, cb) {
return cb(null, this.addresses);
}
sendAsync(payload, callback) {
let method = this.asyncMethods[payload.method];
if (method) {
return method.call(method, payload, (err, result) => {
if (err) {
return callback(err);
}
let response = {'id': payload.id, 'jsonrpc': '2.0', 'result': result};
callback(null, response);
});
}
this.engine.sendAsync.apply(this.engine, arguments);
}
send() {
return this.engine.send.apply(this.engine, arguments);
}
}
module.exports = Provider;

View File

@ -13,7 +13,7 @@ var Config = function(options) {
this.blockchainConfig = {};
this.contractsConfig = {};
this.pipelineConfig = {};
this.webServerConfig = {};
this.webServerConfig = options.webServerConfig;
this.chainTracker = {};
this.assetFiles = {};
this.contractsFiles = [];
@ -63,10 +63,7 @@ Config.prototype.loadConfigFiles = function(options) {
this.loadStorageConfigFile();
this.loadCommunicationConfigFile();
this.loadNameSystemConfigFile();
this.loadContractsConfigFile();
this.loadPipelineConfigFile();
this.loadContractsConfigFile();
this.loadExternalContractsFiles();
this.loadWebServerConfigFile();
@ -82,7 +79,6 @@ Config.prototype.reloadConfig = function() {
this.loadStorageConfigFile();
this.loadCommunicationConfigFile();
this.loadNameSystemConfigFile();
this.loadContractsConfigFile();
this.loadPipelineConfigFile();
this.loadContractsConfigFile();
this.loadExternalContractsFiles();
@ -117,10 +113,20 @@ Config.prototype._updateBlockchainCors = function(){
corsParts.push(utils.buildUrlFromConfig(storageConfig.upload));
}
}
// add whisper cors
if(this.communicationConfig && this.communicationConfig.enabled && this.communicationConfig.provider === 'whisper'){
corsParts.push('embark');
}
let cors = corsParts.join(',');
if(blockchainConfig.rpcCorsDomain === 'auto' && cors.length) blockchainConfig.rpcCorsDomain = cors;
if(blockchainConfig.wsOrigins === 'auto' && cors.length) blockchainConfig.wsOrigins = cors;
if(blockchainConfig.rpcCorsDomain === 'auto'){
if(cors.length) blockchainConfig.rpcCorsDomain = cors;
else blockchainConfig.rpcCorsDomain = '';
}
if(blockchainConfig.wsOrigins === 'auto'){
if(cors.length) blockchainConfig.wsOrigins = cors;
else blockchainConfig.wsOrigins = '';
}
};
Config.prototype._mergeConfig = function(configFilePath, defaultConfig, env, enabledByDefault) {
@ -142,6 +148,7 @@ Config.prototype._mergeConfig = function(configFilePath, defaultConfig, env, ena
let config;
if (fs.existsSync(configFilePath + '.js')) {
delete require.cache[fs.dappPath(configFilePath + '.js')];
config = require(fs.dappPath(configFilePath + '.js'));
} else {
config = fs.readJSONSync(configFilePath + '.json');
@ -150,16 +157,15 @@ Config.prototype._mergeConfig = function(configFilePath, defaultConfig, env, ena
if (env) {
return utils.recursiveMerge(configObject['default'] || {}, configObject[env]);
} else {
return configObject;
}
return configObject;
};
Config.prototype._getFileOrOject = function(object, filePath, property) {
if (typeof (this.configDir) === 'object') {
return this.configDir[property];
}
return this.configDir + filePath;
return utils.joinPath(this.configDir, filePath);
};
Config.prototype.loadBlockchainConfigFile = function() {
@ -174,6 +180,16 @@ Config.prototype.loadBlockchainConfigFile = function() {
let configFilePath = this._getFileOrOject(this.configDir, 'blockchain', 'blockchain');
this.blockchainConfig = this._mergeConfig(configFilePath, configObject, this.env, true);
if (!configFilePath) {
this.blockchainConfig.default = true;
}
if (!this.blockchainConfig.account && !this.blockchainConfig.isDev &&
this.env !== 'development' && this.env !== 'test') {
this.logger.warn(
__('Account settings are needed for this chain.' +
' Please put a valid address and possibly a password in your blockchain config or use a dev chain.')
);
}
};
Config.prototype.loadContractsConfigFile = function() {
@ -209,7 +225,6 @@ Config.prototype.loadContractsConfigFile = function() {
const newContractsConfig = this._mergeConfig(configFilePath, configObject, this.env);
if (!deepEqual(newContractsConfig, this.contractsConfig)) {
this.events.emit(constants.events.contractConfigChanged, newContractsConfig);
this.contractsConfig = newContractsConfig;
}
};
@ -239,7 +254,7 @@ Config.prototype.loadExternalContractsFiles = function() {
};
Config.prototype.loadStorageConfigFile = function() {
var versions = utils.recursiveMerge({"ipfs-api": "17.2.4", "p-iteration": "1.1.7"}, this.embarkConfig.versions || {});
var versions = utils.recursiveMerge({"ipfs-api": "17.2.4"}, this.embarkConfig.versions || {});
var configObject = {
"default": {
@ -267,9 +282,7 @@ Config.prototype.loadNameSystemConfigFile = function() {
// todo: spec out names for registration in the file itself for a dev chain
var configObject = {
"default": {
"available_providers": ["ens"],
"provider": "ens",
"enabled": true
"enabled": false
}
};
@ -301,20 +314,46 @@ Config.prototype.loadWebServerConfigFile = function() {
var configObject = {
"enabled": true,
"host": defaultHost,
"openBrowser": true,
"port": 8000
};
let configFilePath = this._getFileOrOject(this.configDir, 'webserver', 'webserver');
this.webServerConfig = this._mergeConfig(configFilePath, configObject, false);
let webServerConfig = this._mergeConfig(configFilePath, configObject, false);
if (configFilePath === false) {
this.webServerConfig = {enabled: false};
return;
}
if (this.webServerConfig) {
// cli flags to `embark run` should override configFile and defaults (configObject)
this.webServerConfig = utils.recursiveMerge(webServerConfig, this.webServerConfig);
} else {
this.webServerConfig = webServerConfig;
}
};
Config.prototype.loadEmbarkConfigFile = function() {
var configObject = {
options: {
solc: {
"optimize": true,
"optimize-runs": 200
}
}
};
this.embarkConfig = utils.recursiveMerge(configObject, this.embarkConfig);
const contracts = this.embarkConfig.contracts;
const newContractsFiles = this.loadFiles(contracts);
if (!this.contractFiles || newContractsFiles.length !== this.contractFiles.length || !deepEqual(newContractsFiles, this.contractFiles)) {
this.events.emit(constants.events.contractFilesChanged, newContractsFiles);
this.contractsFiles = newContractsFiles;
this.contractsFiles = this.contractsFiles.concat(newContractsFiles).filter((file, index, arr) => {
return !arr.some((file2, index2) => {
return file.filename === file2.filename && index < index2;
});
});
}
// determine contract 'root' directories
this.contractDirectories = contracts.map((dir) => {

View File

@ -6,7 +6,6 @@ const IPC = require('./ipc');
class Engine {
constructor(options) {
this.env = options.env;
this.isDev = options.isDev;
this.client = options.client;
this.locale = options.locale;
this.embarkConfig = options.embarkConfig;
@ -17,9 +16,13 @@ class Engine {
this.events = options.events;
this.context = options.context;
this.useDashboard = options.useDashboard;
this.webServerConfig = options.webServerConfig;
this.webpackConfigName = options.webpackConfigName;
this.ipcRole = options.ipcRole || 'client';
}
init(_options) {
init(_options, callback) {
callback = callback || function() {};
const Events = require('./events.js');
const Logger = require('./logger.js');
const Config = require('./config.js');
@ -27,39 +30,25 @@ class Engine {
let options = _options || {};
this.events = options.events || this.events || new Events();
this.logger = options.logger || new Logger({logLevel: options.logLevel || this.logLevel || 'debug', events: this.events, logFile: this.logFile});
this.config = new Config({env: this.env, logger: this.logger, events: this.events, context: this.context});
this.config = new Config({env: this.env, logger: this.logger, events: this.events, context: this.context, webServerConfig: this.webServerConfig});
this.config.loadConfigFiles({embarkConfig: this.embarkConfig, interceptLogs: this.interceptLogs});
this.plugins = this.config.plugins;
this.isDev = this.config && this.config.blockchainConfig && (this.config.blockchainConfig.isDev || this.config.blockchainConfig.default);
if (this.interceptLogs || this.interceptLogs === undefined) {
this.doInterceptLogs();
utils.interceptLogs(console, this.logger);
}
}
doInterceptLogs() {
var self = this;
let context = {};
context.console = console;
context.console.log = function() {
self.logger.info(utils.normalizeInput(arguments));
};
context.console.warn = function() {
self.logger.warn(utils.normalizeInput(arguments));
};
context.console.info = function() {
self.logger.info(utils.normalizeInput(arguments));
};
context.console.debug = function() {
// TODO: ue JSON.stringify
self.logger.debug(utils.normalizeInput(arguments));
};
context.console.trace = function() {
self.logger.trace(utils.normalizeInput(arguments));
};
context.console.dir = function() {
self.logger.dir(utils.normalizeInput(arguments));
};
this.ipc = new IPC({logger: this.logger, ipcRole: this.ipcRole});
if (this.ipc.isClient()) {
return this.ipc.connect((_err) => {
callback();
});
} else if (this.ipc.isServer()) {
this.ipc.serve();
return callback();
}
callback();
}
registerModule(moduleName, options) {
@ -78,9 +67,14 @@ class Engine {
"fileWatcher": this.fileWatchService,
"webServer": this.webServerService,
"namingSystem": this.namingSystem,
"console": this.console,
"web3": this.web3Service,
"libraryManager": this.libraryManagerService,
"storage": this.storageService
"processManager": this.processManagerService,
"storage": this.storageService,
"graph": this.graphService,
"codeCoverage": this.codeCoverageService,
"pluginCommand": this.pluginCommandService
};
let service = services[serviceName];
@ -94,17 +88,32 @@ class Engine {
return service.apply(this, [options]);
}
processManagerService(_options) {
const ProcessManager = require('./processes/processManager.js');
this.processManager = new ProcessManager({
events: this.events,
logger: this.logger,
plugins: this.plugins
});
}
graphService(_options) {
this.registerModule('graph');
}
pipelineService(_options) {
const self = this;
this.events.emit("status", "Building Assets");
const Pipeline = require('../pipeline/pipeline.js');
const pipeline = new Pipeline({
env: this.env,
buildDir: this.config.buildDir,
contractsFiles: this.config.contractsFiles,
assetFiles: this.config.assetFiles,
events: this.events,
logger: this.logger,
plugins: this.plugins
plugins: this.plugins,
webpackConfigName: this.webpackConfigName
});
this.events.on('code-generator-ready', function () {
self.events.request('code', function (abi, contractsJSON) {
@ -125,37 +134,43 @@ class Engine {
this.servicesMonitor.startMonitor();
}
pluginCommandService() {
this.registerModule('plugin_cmd', {embarkConfigFile: this.embarkConfig, embarkConfig: this.config.embarkConfig, packageFile: 'package.json'});
}
namingSystem(_options) {
this.registerModule('ens');
}
console(_options) {
this.registerModule('console', {
events: this.events,
plugins: this.plugins,
version: this.version,
ipc: this.ipc,
logger: this.logger,
config: this.config
});
}
codeRunnerService(_options) {
const CodeRunner = require('../coderunner/codeRunner.js');
const CodeRunner = require('./modules/coderunner/codeRunner.js');
this.codeRunner = new CodeRunner({
config: this.config,
plugins: this.plugins,
events: this.events,
logger: this.logger
logger: this.logger,
ipc: this.ipc
});
}
codeGeneratorService(_options) {
let self = this;
const CodeGenerator = require('../contracts/code_generator.js');
this.codeGenerator = new CodeGenerator({
blockchainConfig: self.config.blockchainConfig,
contractsConfig: self.config.contractsConfig,
plugins: self.plugins,
storageConfig: self.config.storageConfig,
namesystemConfig: self.config.namesystemConfig,
communicationConfig: self.config.communicationConfig,
events: self.events,
env: self.env
});
this.codeGenerator.listenToCommands();
this.registerModule('code_generator', {plugins: self.plugins, env: self.env});
const generateCode = function () {
self.codeGenerator.buildEmbarkJS(function() {
self.events.request("code-generator:embarkjs:build", () => {
self.events.emit('code-generator-ready');
});
};
@ -175,58 +190,22 @@ class Engine {
deploymentService(options) {
let self = this;
const Compiler = require('../contracts/compiler.js');
let compiler = new Compiler({plugins: self.plugins, logger: self.logger});
this.events.setCommandHandler("compiler:contracts", function(contractFiles, cb) {
compiler.compile_contracts(contractFiles, cb);
});
this.ipc = new IPC({logger: this.logger, ipcRole: options.ipcRole});
if (this.ipc.isServer()) {
this.ipc.serve();
}
this.registerModule('solidity', {ipc: this.ipc, useDashboard: this.useDashboard});
this.registerModule('compiler', {plugins: self.plugins, disableOptimizations: options.disableOptimizations});
this.registerModule('solidity', {ipc: self.ipc, useDashboard: this.useDashboard});
this.registerModule('vyper');
this.registerModule('profiler');
this.registerModule('fuzzer');
this.registerModule('deploytracker');
this.registerModule('deploytracker', {trackContracts: options.trackContracts});
this.registerModule('specialconfigs');
this.registerModule('console_listener', {ipc: this.ipc});
const ContractsManager = require('../contracts/contracts.js');
this.contractsManager = new ContractsManager({
contractFiles: this.config.contractsFiles,
contractsConfig: this.config.contractsConfig,
logger: this.logger,
plugins: this.plugins,
gasLimit: false,
events: this.events
});
const DeployManager = require('../contracts/deploy_manager.js');
this.deployManager = new DeployManager({
blockchain: this.blockchain,
config: this.config,
logger: this.logger,
plugins: this.plugins,
events: this.events,
onlyCompile: options.onlyCompile
});
const ContractDeployer = require('../contracts/contract_deployer.js');
this.contractDeployer = new ContractDeployer({
blockchain: this.blockchain,
logger: this.logger,
events: this.events,
plugins: this.plugins
});
this.registerModule('console_listener', {ipc: self.ipc});
this.registerModule('contracts_manager');
this.registerModule('deployment', {plugins: this.plugins, onlyCompile: options.onlyCompile});
this.events.on('file-event', function (fileType) {
clearTimeout(self.fileTimeout);
self.fileTimeout = setTimeout(() => {
// TODO: still need to redeploy contracts because the original contracts
// config is being corrupted
self.config.reloadConfig();
if (fileType === 'asset') {
// Throttle file changes so we re-write only once for all files
self.events.emit('asset-changed', self.contractsManager);
@ -234,8 +213,6 @@ class Engine {
// TODO: for now need to deploy on asset changes as well
// because the contractsManager config is corrupted after a deploy
if (fileType === 'contract' || fileType === 'config') {
self.config.reloadConfig();
self.events.request('deploy:contracts', () => {});
}
}, 50);
@ -254,46 +231,33 @@ class Engine {
}
storageService(_options) {
this.registerModule('storage', {
storageConfig: this.config.storageConfig,
webServerConfig: this.config.webServerConfig,
blockchainConfig: this.config.blockchainConfig,
host: _options.host,
port: _options.port,
servicesMonitor: this.servicesMonitor,
events: this.events,
logger: this.logger,
context: this.context
});
this.registerModule('storage', {plugins: this.plugins});
this.registerModule('ipfs');
this.registerModule('swarm');
}
web3Service(options) {
const Blockchain = require('../contracts/blockchain.js');
this.blockchain = new Blockchain({
contractsConfig: this.config.contractsConfig,
blockchainConfig: this.config.blockchainConfig,
events: this.events,
logger: this.logger,
isDev: this.isDev,
this.registerModule('blockchain_process', {
locale: this.locale,
isDev: this.isDev,
ipc: this.ipc
});
this.registerModule('blockchain_connector', {
isDev: this.isDev,
web3: options.web3
});
this.registerModule('whisper', {
// TODO: this should not be needed and should be deducted from the config instead
// the eth provider is not necessary the same as the whisper one
web3: this.blockchain.web3
});
this.registerModule('whisper');
}
libraryManagerService(_options) {
const LibraryManager = require('../versions/library_manager.js');
this.libraryManager = new LibraryManager({
plugins: this.plugins,
config: this.config
});
this.registerModule('library_manager');
}
codeCoverageService(_options) {
this.registerModule('coverage');
}
}
module.exports = Engine;

View File

@ -11,10 +11,14 @@ function log(eventType, eventName) {
if (['end', 'prefinish', 'error', 'new', 'demo', 'block', 'version'].indexOf(eventName) >= 0) {
return;
}
// prevents infinite loop when printing events
if (eventType.indexOf("log") >= 0) {
return;
}
//console.log(eventType, eventName);
}
EventEmitter.prototype._maxListeners = 300;
EventEmitter.prototype._maxListeners = 350;
const _on = EventEmitter.prototype.on;
const _setHandler = EventEmitter.prototype.setHandler;

View File

@ -12,6 +12,7 @@ class File {
this.path = options.path;
this.basedir = options.basedir;
this.resolver = options.resolver;
this.downloadedImports = false;
}
parseFileForImport(content, isHttpContract, callback) {
@ -24,10 +25,11 @@ class File {
// Only supported in Solidity
return callback(null, content);
}
const regex = /import ["|']([-a-zA-Z0-9@:%_+.~#?&\/=]+)["|'];/g;
const regex = /import ["']([-a-zA-Z0-9@:%_+.~#?&\/=]+)["'];/g;
let matches;
const filesToDownload = [];
const pathWithoutFile = path.dirname(self.path);
let newContent = content;
while ((matches = regex.exec(content))) {
const httpFileObj = utils.getExternalContractUrl(matches[1]);
const fileObj = {
@ -36,7 +38,7 @@ class File {
};
if (httpFileObj) {
// 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.url = httpFileObj.url;
@ -49,15 +51,15 @@ class File {
if (self.downloadedImports) {
// We already parsed this file
return callback(null, content);
return callback(null, newContent);
}
self.downloadedImports = true;
async.each(filesToDownload, ((fileObj, eachCb) => {
self.downloadFile(fileObj.fileRelativePath, fileObj.url, (_content) => {
eachCb();
});
}), (err) => {
callback(err, content);
self.downloadedImports = true;
callback(err, newContent);
});
}
@ -97,7 +99,7 @@ class File {
fs.readFile(filename, next);
},
function parseForImports(content, next) {
self.parseFileForImport(content, true, (err) => {
self.parseFileForImport(content.toString(), true, (err) => {
next(err, content);
});
}

View File

@ -11,6 +11,18 @@ function mkdirp() {
return fs.mkdirp.apply(fs.mkdirp, arguments);
}
function readdir() {
return fs.readdir.apply(fs.readdir, arguments);
}
function stat() {
return fs.stat.apply(fs.stat, arguments);
}
function remove() {
return fs.remove.apply(fs.remove, arguments);
}
function copy() {
return fs.copy.apply(fs.copy, arguments);
}
@ -77,22 +89,38 @@ function removeSync() {
return fs.removeSync.apply(fs.removeSync, arguments);
}
// returns embarks root directory
function embarkPath(fileOrDir) {
return utils.joinPath(__dirname, '/../../', fileOrDir);
function anchoredPath(envAnchor, ...args) {
const anchor = process.env[envAnchor];
if (!anchor) {
console.error(`process.env.${envAnchor} was not set`.bold.red);
process.exit(1);
}
return utils.joinPath(anchor, ...args);
}
function embarkPath() {
return anchoredPath('EMBARK_PATH', ...arguments);
}
function dappPath() {
return utils.joinPath(utils.pwd(), ...arguments);
return anchoredPath('DAPP_PATH', ...arguments);
}
function createWriteStream() {
return fs.createWriteStream.apply(fs.createWriteStream, arguments);
}
function tmpDir() {
let os = require('os');
return utils.joinPath(os.tmpdir(), ...arguments);
}
module.exports = {
mkdirpSync,
mkdirp,
readdir,
stat,
remove,
copy,
copySync,
move,
@ -110,5 +138,6 @@ module.exports = {
removeSync,
embarkPath,
dappPath,
createWriteStream
createWriteStream,
tmpDir
};

36
lib/core/i18n/i18n.js Normal file
View File

@ -0,0 +1,36 @@
const i18n = require('i18n');
const osLocale = require('os-locale');
const path = require('path');
const supported_languages = ['en', 'pt', 'fr', 'es'];
const default_language = 'en';
i18n.configure({
locales: supported_languages,
register: global,
updateFiles: false,
syncFiles: false,
directory: path.join(__dirname, '../../../', 'locales')
});
function isSupported(locale) {
return (supported_languages.indexOf(locale.substr(0, 2)) >= 0);
}
function setOrDetectLocale(locale) {
const how = locale ? 'specified' : 'detected';
let _locale = locale || osLocale.sync();
_locale = _locale.substr(0, 2);
if (_locale && !isSupported(_locale)) {
console.warn(`===== locale ${_locale} ${how} but not supported, default: en =====`.yellow);
return;
}
return i18n.setLocale(_locale);
}
i18n.setLocale(default_language);
module.exports = {
i18n: i18n,
setOrDetectLocale: setOrDetectLocale
};

View File

@ -1,12 +1,13 @@
let fs = require('./fs.js');
let ipc = require('node-ipc');
const fs = require('./fs.js');
const ipc = require('node-ipc');
const {parse, stringify} = require('flatted/cjs');
class IPC {
constructor(options) {
this.logger = options.logger;
this.socketPath = options.socketPath || fs.dappPath(".embark/embark.ipc");
this.ipcRole = options.ipcRole || "server";
this.ipcRole = options.ipcRole;
ipc.config.silent = true;
this.connected = false;
}
@ -38,6 +39,7 @@ class IPC {
}
serve() {
fs.mkdirpSync(fs.dappPath(".embark"));
ipc.serve(this.socketPath, () => {});
ipc.server.start();
@ -46,27 +48,40 @@ class IPC {
on(action, done) {
const self = this;
ipc.server.on('message', function(data, socket) {
if (data.action !== action) {
ipc.server.on('message', function(messageString, socket) {
const message = parse(messageString);
if (message.action !== action) {
return;
}
let reply = function(replyData) {
self.reply(socket, 'compile', replyData);
let reply = function(error, replyData) {
self.reply(socket, action, error, replyData);
};
done(data.message, reply, socket);
done(message.data, reply, socket);
});
}
reply(client, action, data) {
ipc.server.emit(client, 'message', {action: action, message: data});
reply(client, action, error, data) {
const message = stringify({action, data, error: (error && error.stack)});
ipc.server.emit(client, 'message', message);
}
listenTo(action, callback) {
ipc.of['embark'].on(action, (messageString) => {
callback(parse(messageString));
});
}
broadcast(action, data) {
ipc.server.broadcast(action, stringify(data));
}
once(action, cb) {
ipc.of['embark'].once('message', function(msg) {
if (msg.action !== action) {
ipc.of['embark'].once('message', function(messageString) {
const message = parse(messageString);
if (message.action !== action) {
return;
}
cb(msg.message);
cb(message.error, message.data);
});
}
@ -74,7 +89,7 @@ class IPC {
if (cb) {
this.once(action, cb);
}
ipc.of['embark'].emit('message', {action: action, message: data});
ipc.of['embark'].emit('message', stringify({action: action, data: data}));
}
isClient() {

View File

@ -11,57 +11,57 @@ class Logger {
}
}
Logger.prototype.writeToFile = function (txt) {
Logger.prototype.writeToFile = function () {
if (!this.logFile) {
return;
}
fs.appendFileSync(this.logFile, "\n" + txt);
fs.appendFileSync(this.logFile, "\n" + Array.from(arguments).join(' '));
};
Logger.prototype.error = function (txt) {
if (!txt || !(this.shouldLog('error'))) {
Logger.prototype.error = function () {
if (!arguments.length || !(this.shouldLog('error'))) {
return;
}
this.events.emit("log", "error", txt);
this.logFunction(txt.red);
this.writeToFile("[error]: " + txt);
this.events.emit("log", "error", ...arguments);
this.logFunction(...Array.from(arguments).map(t => { return t ? t.red : t; }));
this.writeToFile("[error]: ", ...arguments);
};
Logger.prototype.warn = function (txt) {
if (!txt || !(this.shouldLog('warn'))) {
Logger.prototype.warn = function () {
if (!arguments.length || !(this.shouldLog('warn'))) {
return;
}
this.events.emit("log", "warning", txt);
this.logFunction(txt.yellow);
this.writeToFile("[warning]: " + txt);
this.events.emit("log", "warning", ...arguments);
this.logFunction(...Array.from(arguments).map(t => { return t ? t.yellow : t; }));
this.writeToFile("[warning]: ", ...arguments);
};
Logger.prototype.info = function (txt) {
if (!txt || !(this.shouldLog('info'))) {
Logger.prototype.info = function () {
if (!arguments.length || !(this.shouldLog('info'))) {
return;
}
this.events.emit("log", "info", txt);
this.logFunction(txt.green);
this.writeToFile("[info]: " + txt);
this.events.emit("log", "info", ...arguments);
this.logFunction(...Array.from(arguments).map(t => { return t ? t.green : t; }));
this.writeToFile("[info]: ", ...arguments);
};
Logger.prototype.debug = function (txt) {
if (!txt || !(this.shouldLog('debug'))) {
Logger.prototype.debug = function () {
if (!arguments.length || !(this.shouldLog('debug'))) {
return;
}
this.events.emit("log", "debug", txt);
this.logFunction(txt);
this.writeToFile("[debug]: " + txt);
this.events.emit("log", "debug", ...arguments);
this.logFunction(...arguments);
this.writeToFile("[debug]: ", ...arguments);
};
Logger.prototype.trace = function (txt) {
if (!txt || !(this.shouldLog('trace'))) {
Logger.prototype.trace = function () {
if (!arguments.length || !(this.shouldLog('trace'))) {
return;
}
this.events.emit("log", "trace", txt);
this.logFunction(txt);
this.writeToFile("[trace]: " + txt);
this.events.emit("log", "trace", ...arguments);
this.logFunction(...arguments);
this.writeToFile("[trace]: ", ...arguments);
};
Logger.prototype.dir = function (txt) {

View File

@ -0,0 +1,101 @@
const RunCode = require('./runCode.js');
const Utils = require('../../../utils/utils');
class CodeRunner {
constructor(options) {
this.config = options.config;
this.plugins = options.plugins;
this.logger = options.logger;
this.events = options.events;
this.ipc = options.ipc;
this.commands = [];
this.runCode = new RunCode({logger: this.logger});
this.registerIpcEvents();
this.IpcClientListen();
this.registerEvents();
this.registerCommands();
}
registerIpcEvents() {
if (!this.ipc.isServer()) {
return;
}
this.ipc.on('runcode:getCommands', (_err, callback) => {
let result = {web3Config: this.runCode.getWeb3Config(), commands: this.commands};
callback(null, result);
});
}
IpcClientListen() {
if (!this.ipc.isClient() || !this.ipc.connected) {
return;
}
this.ipc.listenTo('runcode:newCommand', (command) => {
if (command.varName) {
this.events.emit("runcode:register", command.varName, command.code);
} else {
this.events.request("runcode:eval", command.code);
}
});
}
registerEvents() {
this.events.on("runcode:register", this.registerVar.bind(this));
}
registerCommands() {
this.events.setCommandHandler('runcode:getContext', (cb) => {
cb(this.runCode.context);
});
this.events.setCommandHandler('runcode:eval', this.evalCode.bind(this));
}
registerVar(varName, code, toRecord = true) {
if (this.ipc.isServer() && toRecord) {
this.commands.push({varName, code});
this.ipc.broadcast("runcode:newCommand", {varName, code});
}
this.runCode.registerVar(varName, code);
}
async evalCode(code, cb, forConsoleOnly = false) {
cb = cb || function() {};
const awaitIdx = code.indexOf('await');
let awaiting = false;
if (awaitIdx > -1) {
awaiting = true;
const instructions = Utils.compact(code.split(';'));
const last = instructions.pop();
if (!last.trim().startsWith('return')) {
instructions.push(`return ${last}`);
} else {
instructions.push(last);
}
code = `(async function() {${instructions.join(';')}})();`;
}
let result = this.runCode.doEval(code);
if (forConsoleOnly && this.ipc.isServer()) {
this.commands.push({code});
this.ipc.broadcast("runcode:newCommand", {code});
}
if (!awaiting) {
return cb(null, result);
}
try {
const value = await result;
cb(null, value);
} catch (error) {
cb(error);
}
}
}
module.exports = CodeRunner;

View File

@ -0,0 +1,44 @@
const vm = require('vm');
class RunCode {
constructor({logger}) {
this.logger = logger;
this.context = Object.assign({}, {
global, console, exports, require, module, __filename, __dirname, process,
setTimeout, setInterval, clearTimeout, clearInterval
});
}
doEval(code) {
try {
return vm.runInNewContext(code, this.context);
} catch(e) {
this.logger.error(e.message);
}
}
registerVar(varName, code) {
// TODO: Update all the code being dependent of web3
// To identify, look at the top of the file for something like:
// /*global web3*/
if (varName === 'web3') {
global.web3 = code;
}
this.context["global"][varName] = code;
this.context[varName] = code;
}
getWeb3Config() {
const Web3 = require('web3');
const provider = this.context.web3.currentProvider;
let providerUrl;
if(provider instanceof Web3.providers.HttpProvider){
providerUrl = provider.host;
} else if (provider instanceof Web3.providers.WebsocketProvider) {
providerUrl = provider.connection._url;
}
return {defaultAccount: this.context.web3.eth.defaultAccount, providerUrl: providerUrl};
}
}
module.exports = RunCode;

View File

@ -1,7 +1,6 @@
const fs = require('./fs.js');
const utils = require('../utils/utils.js');
const constants = require('../constants');
const _ = require('underscore');
// TODO: pass other params like blockchainConfig, contract files, etc..
var Plugin = function(options) {
@ -26,10 +25,12 @@ var Plugin = function(options) {
this.imports = [];
this.embarkjs_code = [];
this.embarkjs_init_code = {};
this.embarkjs_init_console_code = {};
this.afterContractsDeployActions = [];
this.onDeployActions = [];
this.eventActions = {};
this.logger = options.logger;
this._loggerObject = options.logger;
this.logger = this._loggerObject; // Might get changed if we do intercept
this.events = options.events;
this.config = options.config;
this.env = options.env;
@ -45,6 +46,22 @@ var Plugin = function(options) {
}
};
Plugin.prototype._log = function(type) {
this._loggerObject[type](this.name + ':', ...[].slice.call(arguments, 1));
};
Plugin.prototype.setUpLogger = function () {
this.logger = {
log: this._log.bind(this, 'log'),
warn: this._log.bind(this, 'warn'),
error: this._log.bind(this, 'error'),
info: this._log.bind(this, 'info'),
debug: this._log.bind(this, 'debug'),
trace: this._log.bind(this, 'trace'),
dir: this._log.bind(this, 'dir')
};
};
Plugin.prototype.isContextValid = function() {
if (this.currentContext.includes(constants.contexts.any) || this.acceptedContext.includes(constants.contexts.any)) {
return true;
@ -65,7 +82,7 @@ Plugin.prototype.loadPlugin = function() {
}
this.loaded = true;
if (this.shouldInterceptLogs) {
this.interceptLogs(this.pluginModule);
this.setUpLogger();
}
(this.pluginModule.call(this, this));
};
@ -85,37 +102,6 @@ Plugin.prototype.pathToFile = function(filename) {
return utils.joinPath(this.pluginPath, filename);
};
Plugin.prototype.interceptLogs = function(context) {
var self = this;
// TODO: this is a bit nasty, figure out a better way
context.console = context.console || console;
//context.console.error = function(txt) {
// // TODO: logger should support an array instead of a single argument
// //self.logger.error.apply(self.logger, arguments);
// self.logger.error(self.name + " > " + txt);
//};
context.console.log = function(txt) {
self.logger.info(self.name + " > " + txt);
};
context.console.warn = function(txt) {
self.logger.warn(self.name + " > " + txt);
};
context.console.info = function(txt) {
self.logger.info(self.name + " > " + txt);
};
context.console.debug = function(txt) {
// TODO: ue JSON.stringify
self.logger.debug(self.name + " > " + txt);
};
context.console.trace = function(txt) {
self.logger.trace(self.name + " > " + txt);
};
context.console.dir = function(txt) {
self.logger.dir(txt);
};
};
// TODO: add deploy provider
Plugin.prototype.registerClientWeb3Provider = function(cb) {
this.clientWeb3Providers.push(cb);
@ -163,7 +149,7 @@ Plugin.prototype.has = function(pluginType) {
Plugin.prototype.addPluginType = function(pluginType) {
this.pluginTypes.push(pluginType);
this.pluginTypes = _.uniq(this.pluginTypes);
this.pluginTypes = Array.from(new Set(this.pluginTypes));
};
Plugin.prototype.generateProvider = function(args) {
@ -204,6 +190,12 @@ Plugin.prototype.addProviderInit = function(providerType, code, initCondition) {
this.addPluginType('initCode');
};
Plugin.prototype.addConsoleProviderInit = function(providerType, code, initCondition) {
this.embarkjs_init_console_code[providerType] = this.embarkjs_init_console_code[providerType] || [];
this.embarkjs_init_console_code[providerType].push([code, initCondition]);
this.addPluginType('initConsoleCode');
};
Plugin.prototype.registerImportFile = function(importName, importLocation) {
this.imports.push([importName, importLocation]);
this.addPluginType('imports');
@ -238,9 +230,8 @@ Plugin.prototype.runPipeline = function(args) {
var shouldRunPipeline = utils.fileMatchesPattern(pipeline.matcthingFiles, args.targetFile);
if (shouldRunPipeline) {
return pipeline.cb.call(this, args);
} else {
return args.source;
}
return args.source;
};
module.exports = Plugin;

View File

@ -1,6 +1,5 @@
const async = require('async');
var Plugin = require('./plugin.js');
var utils = require('../utils/utils.js');
var fs = require('../core/fs.js');
var Plugins = function(options) {
@ -75,7 +74,7 @@ Plugins.prototype.loadInternalPlugin = function(pluginName, pluginConfig) {
};
Plugins.prototype.loadPlugin = function(pluginName, pluginConfig) {
var pluginPath = utils.joinPath(utils.pwd(), 'node_modules', pluginName);
var pluginPath = fs.dappPath('node_modules', pluginName);
var plugin = require(pluginPath);
var pluginWrapper = new Plugin({
@ -138,15 +137,18 @@ Plugins.prototype.runActionsForEvent = function(eventName, args, cb) {
let actionPlugins = this.getPluginsProperty('eventActions', 'eventActions', eventName);
if (actionPlugins.length === 0) {
return cb();
return cb(args);
}
async.eachLimit(actionPlugins, 1, function(plugin, nextEach) {
async.reduce(actionPlugins, args, function(current_args, plugin, nextEach) {
if (typeof (args) === 'function') {
plugin.call(plugin, nextEach);
plugin.call(plugin, (params) => {
nextEach(null, (params || current_args));
});
} else {
//plugin.call(plugin, ...args, nextEach);
plugin.call(plugin, args, nextEach);
plugin.call(plugin, args, (params) => {
nextEach(null, (params || current_args));
});
}
}, cb);
};

View File

@ -1,5 +1,5 @@
const uuid = require('uuid/v1');
const constants = require('../constants');
const constants = require('../../constants');
class Events {

View File

@ -1,8 +1,9 @@
const child_process = require('child_process');
const constants = require('../constants');
const constants = require('../../constants');
const path = require('path');
const utils = require('../utils/utils');
const utils = require('../../utils/utils');
let processCount = 1;
class ProcessLauncher {
/**
@ -15,7 +16,14 @@ class ProcessLauncher {
*/
constructor(options) {
this.name = path.basename(options.modulePath);
this.process = child_process.fork(options.modulePath);
if (this._isDebug()) {
const childOptions = {stdio: 'pipe', execArgv: ['--inspect-brk=' + (60000 + processCount)]};
processCount++;
this.process = child_process.fork(options.modulePath, [], childOptions);
} else {
this.process = child_process.fork(options.modulePath);
}
this.logger = options.logger;
this.events = options.events;
this.silent = options.silent;
@ -25,10 +33,18 @@ class ProcessLauncher {
this._subscribeToMessages();
}
_isDebug() {
const argvString= process.execArgv.join();
return argvString.includes('--debug') || argvString.includes('--inspect');
}
// Subscribes to messages from the child process and delegates to the right methods
_subscribeToMessages() {
const self = this;
this.process.on('message', (msg) => {
if (msg.error) {
self.logger.error(msg.error);
}
if (msg.result === constants.process.log) {
return self._handleLog(msg);
}

View File

@ -0,0 +1,34 @@
class ProcessManager {
constructor(options) {
const self = this;
this.logger = options.logger;
this.events = options.events;
this.plugins = options.plugins;
this.processes = {};
self.events.setCommandHandler('processes:register', (name, cb) => {
this.processes[name] = {
state: 'unstarted',
cb: cb
};
});
self.events.setCommandHandler('processes:launch', (name, cb) => {
let process = self.processes[name];
if (process.state !== 'unstarted') {
return cb();
}
process.state = 'starting';
process.cb.apply(process.cb, [
() => {
process.state = 'running';
cb();
}
]);
});
}
}
module.exports = ProcessManager;

View File

@ -1,10 +1,9 @@
const constants = require('../constants');
const Events = require('./eventsWrapper');
process.on('uncaughtException', function(e){
process.send({error: e.stack});
});
// Set PWD to CWD since Windows doesn't have a value for PWD
if (!process.env.PWD) {
process.env.PWD = process.cwd();
}
const constants = require('../../constants');
const Events = require('./eventsWrapper');
class ProcessWrapper {
@ -13,13 +12,15 @@ class ProcessWrapper {
* Manages the log interception so that all console.* get sent back to the parent process
* Also creates an Events instance. To use it, just do `this.events.[on|request]`
*
* @param {Options} _options Nothing for now
* @param {Options} options pingParent: true by default
*/
constructor(_options) {
constructor(options = {}) {
this.options = Object.assign({pingParent: true}, options);
this.interceptLogs();
this.events = new Events();
this.pingParent();
if(this.options.pingParent) {
this.pingParent();
}
}
// Ping parent to see if it is still alive. Otherwise, let's die
@ -43,7 +44,7 @@ class ProcessWrapper {
} catch (e) {
error();
}
}, 2000);
}, 500);
}
interceptLogs() {
@ -61,7 +62,7 @@ class ProcessWrapper {
_log(type, ...messages) {
const isHardSource = messages.some(message => {
return (typeof message === 'string' && message.indexOf('hard-source') > -1);
return (typeof message === 'string' && message.indexOf('hardsource') > -1);
});
if (isHardSource) {
return;

View File

@ -1,127 +0,0 @@
const httpProxy = require('http-proxy');
const http = require('http');
const constants = require('../constants.json');
let commList = {};
let transactions = {};
let receipts = {};
const {canonicalHost, defaultHost} = require('../utils/host');
const parseRequest = function(reqBody){
let jsonO;
try {
jsonO = JSON.parse(reqBody);
} catch(e){
return; // Request is not a json. Do nothing
}
if(jsonO.method === "eth_sendTransaction"){
commList[jsonO.id] = {
type: 'contract-log',
address: jsonO.params[0].to,
data: jsonO.params[0].data
};
} else if(jsonO.method === "eth_getTransactionReceipt"){
if(transactions[jsonO.params[0]]){
transactions[jsonO.params[0]].receiptId = jsonO.id;
receipts[jsonO.id] = transactions[jsonO.params[0]].commListId;
}
}
};
const parseResponse = function(ipc, resBody){
let jsonO;
try {
jsonO = JSON.parse(resBody);
} catch(e) {
return; // Response is not a json. Do nothing
}
if(commList[jsonO.id]){
commList[jsonO.id].transactionHash = jsonO.result;
transactions[jsonO.result] = {commListId: jsonO.id};
} else if(receipts[jsonO.id] && jsonO.result && jsonO.result.blockNumber){
commList[receipts[jsonO.id]].blockNumber = jsonO.result.blockNumber;
commList[receipts[jsonO.id]].gasUsed = jsonO.result.gasUsed;
commList[receipts[jsonO.id]].status = jsonO.result.status;
if(ipc.connected && !ipc.connecting){
ipc.request('log', commList[receipts[jsonO.id]]);
} else {
ipc.connecting = true;
ipc.connect(() => {
ipc.connecting = false;
});
}
delete transactions[commList[receipts[jsonO.id]].transactionHash];
delete receipts[jsonO.id];
delete commList[jsonO.id];
}
};
exports.serve = function(ipc, host, port, ws){
let proxy = httpProxy.createProxyServer({
target: {
host: canonicalHost(host),
port: port + constants.blockchain.servicePortOnProxy
},
ws: ws
});
proxy.on('error', function () {
console.log(__("Error forwarding requests to blockchain/simulator"));
process.exit();
});
proxy.on('proxyRes', (proxyRes) => {
let resBody = [];
proxyRes.on('data', (b) => resBody.push(b));
proxyRes.on('end', function () {
resBody = Buffer.concat(resBody).toString();
if(resBody){
parseResponse(ipc, resBody);
}
});
});
let server = http.createServer((req, res) => {
let reqBody = [];
req.on('data', (b) => { reqBody.push(b); })
.on('end', () => {
reqBody = Buffer.concat(reqBody).toString();
if(reqBody){
parseRequest(reqBody);
}
});
if(!ws){
proxy.web(req, res);
}
});
if(ws){
const WsParser = require('simples/lib/parsers/ws'); // npm install simples
server.on('upgrade', function (req, socket, head) {
proxy.ws(req, socket, head);
});
proxy.on('open', (proxySocket) => {
proxySocket.on('data', (data) => {
parseResponse(ipc, data.toString().substr(data.indexOf("{")));
});
});
proxy.on('proxyReqWs', (proxyReq, req, socket) => {
var parser = new WsParser(0, false);
socket.pipe(parser);
parser.on('frame', function (frame) {
parseRequest(frame.data);
});
});
}
server.listen(port, defaultHost);
};

View File

@ -1,64 +0,0 @@
let utils = require('../utils/utils.js');
class Console {
constructor(options) {
this.events = options.events;
this.plugins = options.plugins;
this.version = options.version;
this.contractsConfig = options.contractsConfig;
}
runCode(code) {
this.events.request('runcode:eval', code);
}
processEmbarkCmd (cmd) {
if (cmd === 'help' || cmd === __('help')) {
let helpText = [
__('Welcome to Embark') + ' ' + this.version,
'',
__('possible commands are:'),
'versions - ' + __('display versions in use for libraries and tools like web3 and solc'),
// TODO: only if the blockchain is actually active!
// will need to pass te current embark state here
'ipfs - ' + __('instantiated js-ipfs object configured to the current environment (available if ipfs is enabled)'),
'web3 - ' + __('instantiated web3.js object configured to the current environment'),
'quit - ' + __('to immediatly exit (alias: exit)'),
'',
__('The web3 object and the interfaces for the deployed contracts and their methods are also available')
];
return helpText.join('\n');
} else if (['quit', 'exit', 'sair', 'sortir', __('quit')].indexOf(cmd) >= 0) {
utils.exit();
}
return false;
}
executeCmd(cmd, callback) {
var pluginCmds = this.plugins.getPluginsProperty('console', 'console');
for (let pluginCmd of pluginCmds) {
let pluginOutput = pluginCmd.call(this, cmd, {});
if (pluginOutput !== false && pluginOutput !== 'false' && pluginOutput !== undefined) return callback(pluginOutput);
}
let output = this.processEmbarkCmd(cmd);
if (output) {
return callback(output);
}
try {
this.events.request('runcode:eval', cmd, (err, result) => {
callback(result);
});
}
catch (e) {
if (e.message.indexOf('not defined') > 0) {
return callback(("error: " + e.message).red + ("\n" + __("Type") + " " + "help".bold + " " + __("to see the list of available commands")).cyan);
} else {
return callback(e.message);
}
}
}
}
module.exports = Console;

View File

@ -1,67 +0,0 @@
let async = require('async');
let windowSize = require('window-size');
let Monitor = require('./monitor.js');
let Console = require('./console.js');
class Dashboard {
constructor(options) {
this.logger = options.logger;
this.events = options.events;
this.plugins = options.plugins;
this.version = options.version;
this.env = options.env;
this.contractsConfig = options.contractsConfig;
this.events.on('firstDeploymentDone', this.checkWindowSize.bind(this));
this.events.on('outputDone', this.checkWindowSize.bind(this));
}
checkWindowSize() {
let size = windowSize.get();
if (size.height < 40 || size.width < 118) {
this.logger.warn(__("tip: you can resize the terminal or disable the dashboard with") + " embark run --nodashboard".bold.underline);
}
}
start(done) {
let console, monitor;
let self = this;
async.waterfall([
function startConsole(callback) {
console = new Console({
events: self.events,
plugins: self.plugins,
version: self.version,
contractsConfig: self.contractsConfig
});
callback();
},
function startMonitor(callback) {
monitor = new Monitor({env: self.env, console: console, events: self.events});
self.logger.logFunction = monitor.logEntry;
self.events.on('contractsState', monitor.setContracts);
self.events.on('status', monitor.setStatus.bind(monitor));
self.events.on('servicesState', monitor.availableServices.bind(monitor));
self.events.setCommandHandler("console:command", monitor.executeCmd.bind(monitor));
self.logger.info('========================'.bold.green);
self.logger.info((__('Welcome to Embark') + ' ' + self.version).yellow.bold);
self.logger.info('========================'.bold.green);
// TODO: do this after monitor is rendered
callback();
}
], function () {
self.console = console;
self.monitor = monitor;
done();
});
}
}
module.exports = Dashboard;

View File

@ -1,42 +0,0 @@
const i18n = require('i18n');
const osLocale = require('os-locale');
const path = require('path');
const supported_languages = ['en', 'pt', 'fr'];
i18n.configure({
locales: supported_languages,
register: global,
updateFiles: false,
directory: path.join(__dirname, 'locales')
});
function isSupported(locale) {
return (supported_languages.indexOf(locale.substr(0, 2)) >= 0);
}
function setLocale(locale) {
i18n.setLocale(locale.substr(0, 2));
}
function setDefaultLocale() {
osLocale().then(setLocale).catch();
}
function setOrDetectLocale(locale) {
if (locale && !isSupported(locale)) {
console.log("===== locale " + locale + " not supported =====");
}
if (locale) {
return i18n.setLocale(locale.substr(0, 2));
}
setDefaultLocale();
}
setDefaultLocale();
module.exports = {
i18n: i18n,
setOrDetectLocale: setOrDetectLocale
};

View File

@ -1,170 +0,0 @@
{
"new_application": "new_application",
"Contract Name": "Contract Name",
"New Application": "New Application",
"create a barebones project meant only for contract development": "create a barebones project meant only for contract development",
"create a working dapp with a SimpleStorage contract": "create a working dapp with a SimpleStorage contract",
"filename to output logs (default: none)": "filename to output logs (default: none)",
"level of logging to display": "level of logging to display",
"deploy and build dapp at ": "deploy and build dapp at ",
"port to run the dev webserver (default: %s)": "port to run the dev webserver (default: %s)",
"host to run the dev webserver (default: %s)": "host to run the dev webserver (default: %s)",
"disable the development webserver": "disable the development webserver",
"simple mode, disables the dashboard": "simple mode, disables the dashboard",
"no colors in case it's needed for compatbility purposes": "no colors in case it's needed for compatbility purposes",
"filename to output logs (default: %s)": "filename to output logs (default: %s)",
"run dapp (default: %s)": "run dapp (default: %s)",
"Use a specific ethereum client or simulator (supported: %s)": "Use a specific ethereum client or simulator (supported: %s)",
"run blockchain server (default: %s)": "run blockchain server (default: %s)",
"run a fast ethereum rpc simulator": "run a fast ethereum rpc simulator",
"use testrpc as the rpc simulator [%s]": "use testrpc as the rpc simulator [%s]",
"port to run the rpc simulator (default: %s)": "port to run the rpc simulator (default: %s)",
"host to run the rpc simulator (default: %s)": "host to run the rpc simulator (default: %s)",
"number of accounts (default: %s)": "number of accounts (default: %s)",
"Amount of ether to assign each test account (default: %s)": "Amount of ether to assign each test account (default: %s)",
"custom gas limit (default: %s)": "custom gas limit (default: %s)",
"run tests": "run tests",
"resets embarks state on this dapp including clearing cache": "resets embarks state on this dapp including clearing cache",
"generates documentation based on the smart contracts configured": "generates documentation based on the smart contracts configured",
"Upload your dapp to a decentralized storage": "Upload your dapp to a decentralized storage",
"output the version number": "output the version number",
"Logs": "Logs",
"Environment": "Environment",
"Status": "Status",
"Available Services": "Available Services",
"Contracts": "Contracts",
"Console": "Console",
"Welcome to Embark": "Welcome to Embark",
"dashboard start": "dashboard start",
"loaded plugins": "loaded plugins",
"loading solc compiler": "loading solc compiler",
"compiling solidity contracts": "compiling solidity contracts",
"%s doesn't have a compatible contract compiler. Maybe a plugin exists for it.": "%s doesn't have a compatible contract compiler. Maybe a plugin exists for it.",
"assuming %s to be an interface": "assuming %s to be an interface",
"{{className}}: couldn't find instanceOf contract {{parentContractName}}": "{{className}}: couldn't find instanceOf contract {{parentContractName}}",
"did you mean \"%s\"?": "did you mean \"%s\"?",
"%s has no code associated": "%s has no code associated",
"deploying contracts": "deploying contracts",
" already deployed at ": " already deployed at ",
"Pending": "Pending",
"Interface or set to not deploy": "Interface or set to not deploy",
"Deployed": "Deployed",
"Address": "Address",
"running beforeDeploy plugin %s .": "running beforeDeploy plugin %s .",
"deploying": "deploying",
"with": "with",
"gas": "gas",
"error deploying": "error deploying",
"due to error": "due to error",
"error deploying contracts": "error deploying contracts",
"finished deploying contracts": "finished deploying contracts",
"error running afterDeploy: ": "error running afterDeploy: ",
"ready to watch file changes": "ready to watch file changes",
"Starting Server": "Starting Server",
"webserver available at": "webserver available at",
"Webserver": "Webserver",
"versions": "versions",
"possible commands are:": "possible commands are:",
"display versions in use for libraries and tools like web3 and solc": "display versions in use for libraries and tools like web3 and solc",
"instantiated web3.js object configured to the current environment": "instantiated web3.js object configured to the current environment",
"to immediatly exit (alias: exit)": "to immediatly exit (alias: exit)",
"The web3 object and the interfaces for the deployed contracts and their methods are also available": "The web3 object and the interfaces for the deployed contracts and their methods are also available",
"versions in use": "versions in use",
"language to use (default: en)": "language to use (default: en)",
"executing": "executing",
"writing file": "writing file",
"errors found while generating": "errors found while generating",
"Looking for documentation? You can find it at": "Looking for documentation? You can find it at",
"Ready": "Ready",
"Graph will not include undeployed contracts": "Graph will not include undeployed contracts",
"Graph will not include functions": "Graph will not include functions",
"Graph will not include events": "Graph will not include events",
"Embark Blockchain Using: %s": "Embark Blockchain Using: %s",
"running: %s": "running: %s",
"Initializing Embark Template....": "Initializing Embark Template....",
"Init complete": "Init complete",
"App ready at ": "App ready at ",
"already initialized": "already initialized",
"deployed at": "deployed at",
"executing onDeploy commands": "executing onDeploy commands",
"executing: ": "executing: ",
"no config file found at %s using default config": "no config file found at %s using default config",
"Development blockchain has changed to use the --dev option.": "Development blockchain has changed to use the --dev option.",
"You can reset your workspace to fix the problem with": "You can reset your workspace to fix the problem with",
"Otherwise, you can change your data directory in blockchain.json (datadir)": "Otherwise, you can change your data directory in blockchain.json (datadir)",
"tip: you can resize the terminal or disable the dashboard with": "tip: you can resize the terminal or disable the dashboard with",
"help": "help",
"quit": "quit",
"Error Compiling/Building contracts: ": "Error Compiling/Building contracts: ",
"file not found, creating it...": "file not found, creating it...",
"{{className}} has code associated to it but it's configured as an instanceOf {{parentContractName}}": "{{className}} has code associated to it but it's configured as an instanceOf {{parentContractName}}",
"downloading {{packageName}} {{version}}....": "downloading {{packageName}} {{version}}....",
"Swarm node is offline...": "Swarm node is offline...",
"Swarm node detected...": "Swarm node detected...",
"Cannot find file %s Please ensure you are running this command inside the Dapp folder": "Cannot find file %s Please ensure you are running this command inside the Dapp folder",
"compiling Vyper contracts": "compiling Vyper contracts",
"Vyper exited with error code ": "Vyper exited with error code ",
"attempted to deploy %s without specifying parameters": "attempted to deploy %s without specifying parameters",
"Ethereum node (version unknown)": "Ethereum node (version unknown)",
"No Blockchain node found": "No Blockchain node found",
"Couldn't connect to an Ethereum node are you sure it's on?": "Couldn't connect to an Ethereum node are you sure it's on?",
"make sure you have an Ethereum node or simulator running. e.g '%s'": "make sure you have an Ethereum node or simulator running. e.g '%s'",
"Embark is building, please wait...": "Embark is building, please wait...",
"finished building DApp and deploying to": "finished building DApp and deploying to",
"Type": "Type",
"to see the list of available commands": "to see the list of available commands",
"instantiated js-ipfs object configured to the current environment (available if ipfs is enabled)": "instantiated js-ipfs object configured to the current environment (available if ipfs is enabled)",
"reset done!": "reset done!",
"finished building": "finished building",
"finished deploying": "finished deploying",
"adding %s to ipfs": "adding %s to ipfs",
"DApp available at": "DApp available at",
"successfully uploaded to ipfs": "successfully uploaded to ipfs",
"Starting Blockchain node in another process": "Starting Blockchain node in another process",
"Blockchain node is ready": "Blockchain node is ready",
"terminating due to error": "terminating due to error",
"Unable to start the blockchain process. Is Geth installed?": "Unable to start the blockchain process. Is Geth installed?",
"Error while downloading the file": "Error while downloading the file",
"Error while loading the content of ": "Error while loading the content of ",
"Installing packages...": "Installing packages...",
"Next steps:": "Next steps:",
"open another console in the same directory and run": "open another console in the same directory and run",
"deploying to swarm!": "deploying to swarm!",
"adding %s to swarm": "adding %s to swarm",
"successfully uploaded to swarm": "successfully uploaded to swarm",
"Cannot upload: {{plaform}} node is not running on {{protocol}}://{{host}}{{port}}.": "Cannot upload: {{platform}} node is not running on {{protocol}}://{{host}}{{port}}.",
"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.",
"For more info go to http://embark.status.im": "For more info go to http://embark.status.im",
"Cannot upload: {{platform}} node is not running on {{protocol}}://{{host}}{{port}}.": "Cannot upload: {{platform}} node is not running on {{protocol}}://{{host}}{{port}}.",
"DApp path length is too long: ": "DApp path length is too long: ",
"WARNING! DApp path length is too long: ": "WARNING! DApp path length is too long: ",
"This is known to cause issues with starting geth, please consider reducing your DApp path's length to 66 characters or less.": "This is known to cause issues with starting geth, please consider reducing your DApp path's length to 66 characters or less.",
"%s is not installed on your machine": "%s is not installed on your machine",
"You can install it by visiting: %s": "You can install it by visiting: %s",
"Starting ipfs process": "Starting ipfs process",
"ipfs process started": "ipfs process started",
"Starting swarm process": "Starting swarm process",
"unknown command": "unknown command",
"Simulator not found; Please install it with \"%s\"": "Simulator not found; Please install it with \"%s\"",
"IMPORTANT: if you using a NodeJS version older than 6.9.1 then you need to install an older version of testrpc \"%s\"": "IMPORTANT: if you using a NodeJS version older than 6.9.1 then you need to install an older version of testrpc \"%s\"",
"swarm process started": "swarm process started",
"Storage process for swarm ended before the end of this process. Code: 12": "Storage process for swarm ended before the end of this process. Code: 12",
"Storage process for swarm ended before the end of this process. Code: 1": "Storage process for swarm ended before the end of this process. Code: 1",
"Storage process for ipfs ended before the end of this process. Code: 12": "Storage process for ipfs ended before the end of this process. Code: 12",
"Blockchain process ended before the end of this process. Code: %s": "Blockchain process ended before the end of this process. Code: %s",
"Cannot start {{platform}} node on {{protocol}}://{{host}}{{port}}.": "Cannot start {{platform}} node on {{protocol}}://{{host}}{{port}}.",
"Cannot upload: {{platform}} node is not running on {{url}}.": "Cannot upload: {{platform}} node is not running on {{url}}.",
"Cannot start {{platform}} node on {{url}}.": "Cannot start {{platform}} node on {{url}}.",
"Storage process for swarm ended before the end of this process. Code: 0": "Storage process for swarm ended before the end of this process. Code: 0",
"error uploading to swarm": "error uploading to swarm",
"IPFS node detected": "IPFS node detected",
"Ethereum node detected": "Ethereum node detected",
"Starting swarm process": "Starting swarm process",
"Deployment Done": "Deployment Done",
"Name your app (default is %s):": "Name your app (default is %s):"
}

View File

@ -1,188 +0,0 @@
{
"New Application": "Nueva Aplicación",
"Contract Name": "Nombre del Contrato",
"Address": "Dirección",
"Status": "Estado",
"Embark Blockchain Using: %s": "Embark Blockchain esta usando el comando: %s",
"running: %s": "ejecutando: %s",
"already initialized": "ya esta inicializado",
"create a barebones project meant only for contract development": "crear un proyecto vacio destinado solo para el desarrollo de contratos",
"loading solc compiler": "cargando el compilador solc",
"Welcome to Embark": "Bienvenido a Embark",
"possible commands are:": "los comandos posibles son:",
"display versions in use for libraries and tools like web3 and solc": "mostrar versiones en uso para bibliotecas y herramientas como web3 y solc",
"instantiated web3.js object configured to the current environment": "objeto web3.js instanciado configurado para el entorno actual",
"to immediatly exit (alias: exit)": "para salir inmediatamente (alias: exit)",
"The web3 object and the interfaces for the deployed contracts and their methods are also available": "El objeto web3 y las interfaces para los contratos publicados y sus métodos también están disponibles",
"create a working dapp with a SimpleStorage contract": "Crear una dapp funcional con un contrato SimpleStorage",
"filename to output logs (default: none)": "fichero/archivo para salida de los logs (predefinido: none)",
"level of logging to display": "nivel de log para mostrar",
"deploy and build dapp at ": "Publica los contractos y construye la aplicación en ",
"port to run the dev webserver (default: %s)": "puerto para correr el servidor web para desarrollo (default: %s)",
"host to run the dev webserver (default: %s)": "host para correr el servidor web para desarrollo (default: %s)",
"disable the development webserver": "desactiva el servidor web para desarrollo",
"simple mode, disables the dashboard": "modo simple, desactiva el dashboard",
"no colors in case it's needed for compatbility purposes": "Sin colores, en caso de ser necessario para compatibilidad con la terminal",
"filename to output logs (default: %s)": "fichero/archivo para los logs (predefinido: %s)",
"run dapp (default: %s)": "ejecutar la dapp (applicación decentralizada) (predefinido: %s)",
"Use a specific ethereum client or simulator (supported: %s)": "Usa un cliente o un simulador de ethereum específico (soportado: %s)",
"run blockchain server (default: %s)": "ejecuta un nodo de blockchain (predefinido: %s)",
"run a fast ethereum rpc simulator": "ejecuta un simulador RPC de ethereum",
"use testrpc as the rpc simulator [%s]": "usa testrpc como simulator de rpc [%s]",
"port to run the rpc simulator (default: %s)": "puerto para ejecutar un simulador de rpc (predefinido: %s)",
"host to run the rpc simulator (default: %s)": "host para ejecutar un servidor de rpc (predefinido: %s)",
"number of accounts (default: %s)": "número de cuentas (predefinido: %s)",
"Amount of ether to assign each test account (default: %s)": "Cantidad de éter para asignar a cada cuenta de prueba (predefinido: %s)",
"custom gas limit (default: %s)": "límite de gas (predefinido: %s)",
"run tests": "ejecutar las pruebas",
"resets embarks state on this dapp including clearing cache": "restablece el estado de Embark en esta dapp, incluida la eliminación de la memoria caché",
"generates documentation based on the smart contracts configured": "genera documentación basada en los contratos inteligentes configurados",
"Upload your dapp to a decentralized storage": "Suba su dapp a un almacenamiento descentralizado",
"output the version number": "mostrar el número de versión",
"Logs": "Logs",
"Environment": "Ambiente",
"Available Services": "Servicios Disponibles",
"Contracts": "Contratos",
"Console": "Consola",
"dashboard start": "inicia el panel de control",
"loaded plugins": "plugins cargados",
"compiling solidity contracts": "Compilando contratos Solidity",
"%s doesn't have a compatible contract compiler. Maybe a plugin exists for it.": "%s no tiene un compilador de contrato compatible. Tal vez existe un plugin para ello.",
"assuming %s to be an interface": "suponiendo que %s sea una interfaz",
"{{className}}: couldn't find instanceOf contract {{parentContractName}}": "{{className}}: no se pudo encontrar la instancia (instanceOf) del contrato {{parentContractName}}",
"did you mean \"%s\"?": "querías decir \"%s\"?",
"%s has no code associated": "%s no tiene código asociado",
"deploying contracts": "publicando contratos",
"running beforeDeploy plugin %s .": "ejecutanto plugin beforeDeploy %s .",
"deploying": "publicando",
"with": "con",
"gas": "gas",
"Pending": "Pendiente",
"Interface or set to not deploy": "Interfaz o configurado para no publicar",
"error deploying": "error publicando",
"due to error": "debido a error",
"error deploying contracts": "error publicando contratos",
"finished deploying contracts": "publicación de contratos concluida",
"error running afterDeploy: ": "error ejecutando afterDeploy: ",
"ready to watch file changes": "listo para monitorear alteraciones en ficheros/archivos",
"Starting Server": "iniciando el servidor",
"webserver available at": "servidor web disponible en",
"Webserver": "Servidor Web",
" already deployed at ": " ya publicado en ",
"Deployed": "Publicado",
"Name must be only letters, spaces, or dashes": "El nombre debe ser solo letras, espacios o guiones",
"Name your app (default is %s)": "Nombre su aplicación (el valor predeterminado es %s)",
"Invalid name": "Nombre inválido",
"unknown command": "comando desconocido",
"did you mean": "querías decir",
"warning: running default config on a non-development environment": "aviso: ejecutando la configuración predefinida en un ambiente de no-desarrollo",
"could not find {{geth_bin}} command; is {{client_name}} installed or in the PATH?": "no se pudo encontrar el comando {{geth_bin}}; ¿Está {{client_name}} instalado o en la RUTA?",
"no accounts found": "no se encontraron cuentas",
"initializing genesis block": "inicializando bloque genesis",
"rpcCorsDomain set to *": "rpcCorsDomain definido como *",
"make sure you know what you are doing": "asegúrate de saber lo que estás haciendo",
"warning: cors is not set": "aviso: cors no está definido",
"wsOrigins set to *": "wsOrigins definido como *",
"warning: wsOrigins is not set": "aviso: wsOrigins no está definido",
"reset done!": "reset listo!",
"%s is not installed on your machine": "%s no está instalado en su máquina",
"You can install it by running: %s": "Puede instalarlo ejecutando: %s",
"Initializing Embark Template....": "Inicializando la plantilla de Embark....",
"Installing packages...": "Instalando paquetes...",
"Init complete": "Init completo",
"App ready at ": "Aplicación lista en ",
"Next steps:": "Próximos pasos:",
"open another console in the same directory and run": "abrir otra consola en el mismo directorio y ejecutar",
"For more info go to http://embark.status.im": "Para más información ve a http://embark.status.im",
"%s : instanceOf is set to itself": "%s : instanceOf se establece a sí mismo",
"{{className}} has code associated to it but it's configured as an instanceOf {{parentContractName}}": "{{className}} tiene un código asociado pero está configurado como una instancia de {{parentContractName}}",
"Error Compiling/Building contracts: ": "Error Compilando/Construyendo los contractos: ",
"Error: ": "Error: ",
"there are two or more contracts that depend on each other in a cyclic manner": "hay dos o más contratos que dependen el uno del otro de forma cíclica",
"Embark couldn't determine which one to deploy first": "Embark no pudo determinar cual publicar primero",
"{{inputName}} has not been defined for {{className}} constructor": "{{inputName}} no ha sido definido para el constructor de {{className}}",
"error deploying %s": "error publicando %s",
"executing onDeploy commands": "ejecutando comandos onDeploy",
"error executing onDeploy for ": "error ejecutando onDeploy para ",
" does not exist": " no existe",
"error running onDeploy: ": "error ejecutando onDeploy: ",
" exists but has been set to not deploy": " existe, pero se ha configurado para no implementar",
"couldn't find a valid address for %s has it been deployed?": "no pudo encontrar una dirección válida para %s ¿ha sido publicado?",
"executing: ": "ejecutando: ",
"the transaction was rejected; this usually happens due to a throw or a require": "la transacción fue rechazada; esto generalmente sucede debido a un throw o a un require",
"no account found at index": "no se encontró una cuenta en index",
" check the config": " verifique la configuración",
"Both \"from\" and \"fromIndex\" are defined for contract": "Ambos \"from\" y \"fromIndex\" están definidos para el contracto",
"Using \"from\" as deployer account.": "Usando \"from\" como cuenta de implementación.",
"{{linkReference}} is too long": "{{linkReference}} es muy largo",
"{{contractName}} needs {{libraryName}} but an address was not found, did you deploy it or configured an address?": "{{contractName}} necesita {{libraryName}} pero no se encontró una dirección, ¿la implementó o configuró una dirección?",
"attempted to deploy %s without specifying parameters": "intentado publicar %s sin especificar parámetros",
"deployed at": "publicado en",
"no contracts found": "no hay contractos encontrados",
"Blockchain component is disabled in the config": "El componente Blockchain está deshabilitado en la configuración",
"Couldn't connect to an Ethereum node are you sure it's on?": "No se pudo conectar a un nodo Ethereum, ¿está seguro de que está activado?",
"make sure you have an Ethereum node or simulator running. e.g '%s'": "asegurese de tener un nodo o simulador Ethereum en funcionamiento. e.g '%s'",
"executing": "ejecutando",
"the transaction was rejected; this usually happens due to a throw or a require, it can also happen due to an invalid operation": "la transacción fue rechazada; esto generalmente ocurre debido a un throw o un require, también puede ocurrir debido a una operación no válida",
"Cannot find file %s Please ensure you are running this command inside the Dapp folder": "No se puede encontrar el archivo %s Asegúrese de que está ejecutando este comando dentro de la carpeta de la DApp",
"no config file found at %s using default config": "archivo de configuración no encontrado en %s utilizado la configuración predefinida",
"HTTP contract file not found": "archivo de contracto HTTP no encontrado",
"contract file not found": "archivo de contracto no encontrado",
"file not found, creating it...": "archivo no encontrado, creándolo ...",
"No Blockchain node found": "No se encontró ningún nodo de Blockchain",
"Ethereum node (version unknown)": "Nodo Ethereum (versión desconocida)",
"this event is deprecated and will be removed in future versions %s": "este evento está en desuso y se eliminará en futuras versiones %s",
"Error while downloading the file": "Error descargando el archivo",
"Plugin {{name}} can only be loaded in the context of \"{{contextes}}\"": "El complemento {{name}} solo se puede cargar en el contexto de \"{{contextes}} \"",
"error running service check": "error al ejecutar la comprobación del servicio",
"help": "ayuda",
"quit": "salir",
"Type": "Tipo",
"to see the list of available commands": "para ver la lista de comandos disponibles",
"Asset Pipeline": "Asset Pipeline",
"Ethereum node detected": "Nodo Ethereum detectado",
"Deployment Done": "Publicación completada",
"Looking for documentation? You can find it at": "¿Buscando documentación? puede encontrarla en",
"Ready": "Listo",
"tip: you can resize the terminal or disable the dashboard with": "consejo: puede redimensionar la terminal o desactivar el tablero con",
"finished building": "construcción completada",
"Done": "Hecho",
"Cannot upload: {{platform}} node is not running on {{url}}.": "No se puede cargar: el nodo {{platform}} no se está ejecutando en {{url}}.",
"try \"{{ipfs}}\" or \"{{swarm}}\"": "intente \"{{ipfs}}\" o \"{{swarm}}\"",
"finished deploying": "publicación completada",
"finished building DApp and deploying to": "construcción de la DApp completada y publicada en",
"IPFS node detected": "Nodo de IPFS detectado",
"IPFS node is offline": "Nodo de IPFS no está en línea",
"not found or not in the path. Guessing %s for path": "no encontrado o no se encuentra en la ruta. Estimando %s para la ruta",
"adding %s to ipfs": "añadiendo %s a ipfs",
"DApp available at": "DApp disponible en",
"error uploading to ipfs": "error cargando a ipfs",
"successfully uploaded to ipfs": "cargado con éxito a ipfs",
"Error while loading the content of ": "Error al cargar el contenido de ",
"error compiling for unknown reasons": "error compilando por razones desconocidas",
"error compiling. There are sources available but no code could be compiled, likely due to fatal errors in the solidity code": "error compilando. Hay fuentes disponibles pero no se pudo compilar ningún código, probablemente debido a errores fatales en el código solidity",
"Swarm node detected...": "nodo Swarm detectado...",
"Swarm node is offline...": "el nodo de Swarm no se encuentra en línea...",
"deploying to swarm!": "publicando en swarm!",
"adding %s to swarm": "añadiendo %s a swarm",
"error uploading to swarm": "error cargando a swarm",
"successfully uploaded to swarm": "cargado exitosamente a swarm",
"Vyper exited with error code ": "Vyper salió con el código de error ",
"Execution returned no result": "La ejecución no devolvió resultados",
"compiling Vyper contracts": "compilando contractos Vyper",
"Webserver is offline": "el servidor web no se encuentra en línea",
"stopping webserver": "deteniendo el servidor web",
"a webserver is already running at": "un servidor web ya se está ejecutando en",
"no webserver is currently running": "ningún servidor web se está ejecutando actualmente",
"couldn't find file": "el archivo no pudo ser encontrado",
"errors found while generating": "errores encontrados al generar",
"writing file": "escribiendo archivo",
"Simulator not found; Please install it with \"%s\"": "Simulador no encontrado; Por favor instalarlo con \"%s\"",
"Tried to load testrpc but an error occurred. This is a problem with testrpc": "Intenté cargar testrpc pero ocurrió un error. Este es un problema con testrpc",
"IMPORTANT: if you using a NodeJS version older than 6.9.1 then you need to install an older version of testrpc \"%s\". Alternatively install node 6.9.1 and the testrpc 3.0": "IMPORTANTE: si usa una versión de NodeJS anterior a la 6.9.1, entonces necesita instalar una versión anterior de testrpc \"%s\". Alternativamente, instale el Node 6.9.1 y el testrpc 3.0",
"terminating due to error": "terminando debido a un error",
"There a a space in the version of {{versionKey}}. We corrected it for you ({{correction})": "Hay un espacio en la versión de {{versionKey}}. Lo corregimos por ti ({{correction})",
"versions": "versiones",
"versions in use": "versiones en uso",
"downloading {{packageName}} {{version}}....": "descargando {{packageName}} {{version}}...."
}

View File

@ -1,188 +0,0 @@
{
"New Application": "Nova Aplicacao",
"Contract Name": "Contracto",
"Address": "Endereço",
"Status": "Estado",
"Embark Blockchain Using: %s": "Embark Blockchain esta usando o commando: %s",
"running: %s": "executando: %s",
"already initialized": "ja esta inicializado",
"create a barebones project meant only for contract development": "criar um projeto vazio destinado apenas ao desenvolvimento de contratos",
"loading solc compiler": "carregando o compilador solc",
"Welcome to Embark": "Bem-vindo ao Embark",
"possible commands are:": "comandos possíveis são:",
"display versions in use for libraries and tools like web3 and solc": "lista versões em uso para bibliotecas e ferramentas como web3 e solc",
"instantiated web3.js object configured to the current environment": "objeto web3.js instanciado configurado para o ambiente atual",
"to immediatly exit (alias: exit)": "para sair imediatamente (alias: exit)",
"The web3 object and the interfaces for the deployed contracts and their methods are also available": "O objeto web3 e as interfaces para os contratos implantados e seus métodos também estão disponíveis",
"create a working dapp with a SimpleStorage contract": "Cria uma dapp funcional com o contrato SimpleStorage",
"filename to output logs (default: none)": "ficheiro/arquivo para saída dos logs (predefinido: none)",
"level of logging to display": "nivel do log",
"deploy and build dapp at ": "Publica os contractos e constroi a applicacao em ",
"port to run the dev webserver (default: %s)": "porta para correr o servidor web para desenvolvimento (default: %s)",
"host to run the dev webserver (default: %s)": "host para correr o servidor web para desenvolvimento (default: %s)",
"disable the development webserver": "disativa o servidor web para desenvolvimento",
"simple mode, disables the dashboard": "modo simples, disativa o dashboard",
"no colors in case it's needed for compatbility purposes": "sem cores, em caso seja necessario para compabitilidade com a terminal",
"filename to output logs (default: %s)": "ficheiro/arquivo para os logs (predefinido: %s)",
"run dapp (default: %s)": "executa a dapp (applicacao decentralizada) (predefinido: %s)",
"Use a specific ethereum client or simulator (supported: %s)": "Usa um cliente ou simulador de ethereum específico (supportado: %s)",
"run blockchain server (default: %s)": "executa un node de blockchain (predefinido: %s)",
"run a fast ethereum rpc simulator": "executa um simulador RPC de ethereum",
"use testrpc as the rpc simulator [%s]": "usa testrpc como simulator de rpc [%s]",
"port to run the rpc simulator (default: %s)": "porta para executar simulador de rpc (predefinido: %s)",
"host to run the rpc simulator (default: %s)": "host para executar servidor de rpc (predefinido: %s)",
"number of accounts (default: %s)": "numero de contas (predefinido: %s)",
"Amount of ether to assign each test account (default: %s)": "Quantidade de éter para atribuir cada conta de teste (predefinido: %s)",
"custom gas limit (default: %s)": "limite de gás (predefinido: %s)",
"run tests": "executar os testes",
"resets embarks state on this dapp including clearing cache": "recomenca o estado do Embark nesta appliacao, incluindo a cache",
"generates documentation based on the smart contracts configured": "gera documentação baseada nos contratos configurados",
"Upload your dapp to a decentralized storage": "Carrega a appliacao para armazenamento descentralizado",
"output the version number": "produz a versão actual",
"Logs": "Logs",
"Environment": "Ambiente",
"Available Services": "Serviços Disponíveis",
"Contracts": "Contratos",
"Console": "Consola",
"dashboard start": "inicia o painel de controle",
"loaded plugins": "plugins carregados",
"compiling solidity contracts": "Compilando contratos Solidity",
"%s doesn't have a compatible contract compiler. Maybe a plugin exists for it.": "%s não tem um compilador de contrato compatível. Talvez exista um plugin para isso.",
"assuming %s to be an interface": "assumindo que %s é uma interface",
"{{className}}: couldn't find instanceOf contract {{parentContractName}}": "{{className}}: não foi possível encontrar instancia de (instanceOf) do contrato {{parentContractName}}",
"did you mean \"%s\"?": "você quis dizer \"%s\"?",
"%s has no code associated": "%s não tem código associado",
"deploying contracts": "publicando contratos",
"running beforeDeploy plugin %s .": "executando plugin beforeDeploy %s .",
"deploying": "publicando",
"with": "com",
"gas": "gas",
"Pending": "Pendente",
"Interface or set to not deploy": "Interface ou configurado para não ser publicado",
"error deploying": "erro de publicação",
"due to error": "devido a erro",
"error deploying contracts": "erro publicando contratos",
"finished deploying contracts": "publicação de contratos concluida",
"error running afterDeploy: ": "erro executado afterDeploy: ",
"ready to watch file changes": "pronto para monitorar alterações em ficheiros/arquivos",
"Starting Server": "iniciando o servidor",
"webserver available at": "servidor web disponivel em",
"Webserver": "Servidor Web",
" already deployed at ": " já publicado em ",
"Deployed": "Publicado",
"Name must be only letters, spaces, or dashes": "O nome deve ser apenas letras, espaços ou traços",
"Name your app (default is %s)": "Nome da aplicacao (predefinido is %s)",
"Invalid name": "Nome inválido",
"unknown command": "comando desconhecido",
"did you mean": "você quis dizer",
"warning: running default config on a non-development environment": "aviso: executando a configuração padrão em um ambiente de não desenvolvimento",
"could not find {{geth_bin}} command; is {{client_name}} installed or in the PATH?": "não foi possível encontrar o comando {{geth_bin}}; o {{client_name}} instalado ou no PATH?",
"no accounts found": "nenhuma conta encontrada",
"initializing genesis block": "inicializando o bloco de gênese",
"rpcCorsDomain set to *": "rpcCorsDomain definido como *",
"make sure you know what you are doing": "certifique-se de saber o que está fazendo",
"warning: cors is not set": "aviso: cors não está definido",
"wsOrigins set to *": "wsOrigins definido como *",
"warning: wsOrigins is not set": "aviso: wsOrigins não está definido",
"reset done!": "reset feito!",
"%s is not installed on your machine": "%s não está instalado na sua máquina",
"You can install it by running: %s": "Você pode instalá-lo executando: %s",
"Initializing Embark Template....": "Inicializando Embark Template....",
"Installing packages...": "Instalando pacotes...",
"Init complete": "Init complete",
"App ready at ": "App ready at ",
"Next steps:": "Next steps:",
"open another console in the same directory and run": "open another console in the same directory and run",
"For more info go to http://embark.status.im": "For more info go to http://embark.status.im",
"%s : instanceOf is set to itself": "%s : instanceOf is set to itself",
"{{className}} has code associated to it but it's configured as an instanceOf {{parentContractName}}": "{{className}} has code associated to it but it's configured as an instanceOf {{parentContractName}}",
"Error Compiling/Building contracts: ": "Error Compiling/Building contracts: ",
"Error: ": "Error: ",
"there are two or more contracts that depend on each other in a cyclic manner": "there are two or more contracts that depend on each other in a cyclic manner",
"Embark couldn't determine which one to deploy first": "Embark couldn't determine which one to deploy first",
"{{inputName}} has not been defined for {{className}} constructor": "{{inputName}} has not been defined for {{className}} constructor",
"error deploying %s": "error deploying %s",
"executing onDeploy commands": "executing onDeploy commands",
"error executing onDeploy for ": "error executing onDeploy for ",
" does not exist": " does not exist",
"error running onDeploy: ": "error running onDeploy: ",
" exists but has been set to not deploy": " exists but has been set to not deploy",
"couldn't find a valid address for %s has it been deployed?": "couldn't find a valid address for %s has it been deployed?",
"executing: ": "executing: ",
"the transaction was rejected; this usually happens due to a throw or a require": "the transaction was rejected; this usually happens due to a throw or a require",
"no account found at index": "no account found at index",
" check the config": " check the config",
"Both \"from\" and \"fromIndex\" are defined for contract": "Both \"from\" and \"fromIndex\" are defined for contract",
"Using \"from\" as deployer account.": "Using \"from\" as deployer account.",
"{{linkReference}} is too long": "{{linkReference}} is too long",
"{{contractName}} needs {{libraryName}} but an address was not found, did you deploy it or configured an address?": "{{contractName}} needs {{libraryName}} but an address was not found, did you deploy it or configured an address?",
"attempted to deploy %s without specifying parameters": "attempted to deploy %s without specifying parameters",
"deployed at": "deployed at",
"no contracts found": "no contracts found",
"Blockchain component is disabled in the config": "Blockchain component is disabled in the config",
"Couldn't connect to an Ethereum node are you sure it's on?": "Couldn't connect to an Ethereum node are you sure it's on?",
"make sure you have an Ethereum node or simulator running. e.g '%s'": "make sure you have an Ethereum node or simulator running. e.g '%s'",
"executing": "executing",
"the transaction was rejected; this usually happens due to a throw or a require, it can also happen due to an invalid operation": "the transaction was rejected; this usually happens due to a throw or a require, it can also happen due to an invalid operation",
"Cannot find file %s Please ensure you are running this command inside the Dapp folder": "Cannot find file %s Please ensure you are running this command inside the Dapp folder",
"no config file found at %s using default config": "no config file found at %s using default config",
"HTTP contract file not found": "HTTP contract file not found",
"contract file not found": "contract file not found",
"file not found, creating it...": "file not found, creating it...",
"No Blockchain node found": "No Blockchain node found",
"Ethereum node (version unknown)": "Ethereum node (version unknown)",
"this event is deprecated and will be removed in future versions %s": "this event is deprecated and will be removed in future versions %s",
"Error while downloading the file": "Error while downloading the file",
"Plugin {{name}} can only be loaded in the context of \"{{contextes}}\"": "Plugin {{name}} can only be loaded in the context of \"{{contextes}}\"",
"error running service check": "error running service check",
"help": "ajuda",
"quit": "sair",
"Type": "Type",
"to see the list of available commands": "to see the list of available commands",
"Asset Pipeline": "Asset Pipeline",
"Ethereum node detected": "Ethereum node detected",
"Deployment Done": "Deployment Done",
"Looking for documentation? You can find it at": "A procura de Documentacao? pode encontra-la em",
"Ready": "Ready",
"tip: you can resize the terminal or disable the dashboard with": "tip: you can resize the terminal or disable the dashboard with",
"finished building": "finished building",
"Done": "Done",
"Cannot upload: {{platform}} node is not running on {{url}}.": "Cannot upload: {{platform}} node is not running on {{url}}.",
"try \"{{ipfs}}\" or \"{{swarm}}\"": "try \"{{ipfs}}\" or \"{{swarm}}\"",
"finished deploying": "finished deploying",
"finished building DApp and deploying to": "finished building DApp and deploying to",
"IPFS node detected": "IPFS node detected",
"IPFS node is offline": "IPFS node is offline",
"not found or not in the path. Guessing %s for path": "not found or not in the path. Guessing %s for path",
"adding %s to ipfs": "adding %s to ipfs",
"DApp available at": "DApp available at",
"error uploading to ipfs": "error uploading to ipfs",
"successfully uploaded to ipfs": "successfully uploaded to ipfs",
"Error while loading the content of ": "Error while loading the content of ",
"error compiling for unknown reasons": "error compiling for unknown reasons",
"error compiling. There are sources available but no code could be compiled, likely due to fatal errors in the solidity code": "error compiling. There are sources available but no code could be compiled, likely due to fatal errors in the solidity code",
"Swarm node detected...": "Swarm node detected...",
"Swarm node is offline...": "Swarm node is offline...",
"deploying to swarm!": "deploying to swarm!",
"adding %s to swarm": "adding %s to swarm",
"error uploading to swarm": "error uploading to swarm",
"successfully uploaded to swarm": "successfully uploaded to swarm",
"Vyper exited with error code ": "Vyper exited with error code ",
"Execution returned no result": "Execution returned no result",
"compiling Vyper contracts": "compiling Vyper contracts",
"Webserver is offline": "Webserver is offline",
"stopping webserver": "stopping webserver",
"a webserver is already running at": "a webserver is already running at",
"no webserver is currently running": "no webserver is currently running",
"couldn't find file": "couldn't find file",
"errors found while generating": "errors found while generating",
"writing file": "escrevendo ficheiro",
"Simulator not found; Please install it with \"%s\"": "Simulator not found; Please install it with \"%s\"",
"Tried to load testrpc but an error occurred. This is a problem with testrpc": "Tried to load testrpc but an error occurred. This is a problem with testrpc",
"IMPORTANT: if you using a NodeJS version older than 6.9.1 then you need to install an older version of testrpc \"%s\". Alternatively install node 6.9.1 and the testrpc 3.0": "IMPORTANT: if you using a NodeJS version older than 6.9.1 then you need to install an older version of testrpc \"%s\". Alternatively install node 6.9.1 and the testrpc 3.0",
"terminating due to error": "terminating due to error",
"There a a space in the version of {{versionKey}}. We corrected it for you ({{correction})": "There a a space in the version of {{versionKey}}. We corrected it for you ({{correction})",
"versions": "versions",
"versions in use": "versions in use",
"downloading {{packageName}} {{version}}....": "downloading {{packageName}} {{version}}...."
}

View File

@ -1,13 +1,3 @@
let async = require('async');
const constants = require('./constants');
require('colors');
// Set PWD to CWD since Windows doesn't have a value for PWD
if (!process.env.PWD) {
process.env.PWD = process.cwd();
}
let version = require('../package.json').version;
class Embark {
@ -29,357 +19,6 @@ class Embark {
this.config.loadConfigFiles(options);
this.plugins = this.config.plugins;
}
isDev(env) {
if (this.config && this.config.blockchainConfig && this.config.blockchainConfig.isDev) {
return true;
} else if (this.config && this.config.blockchainConfig && this.config.blockchainConfig.isDev === false) {
return false;
}
return (env === 'development');
}
blockchain(env, client) {
this.context = [constants.contexts.blockchain];
return require('./cmds/blockchain/blockchain.js')(this.config.blockchainConfig, client, env, this.isDev(env), () => {}).run();
}
simulator(options) {
this.context = options.context || [constants.contexts.simulator, constants.contexts.blockchain];
let Simulator = require('./cmds/simulator.js');
let simulator = new Simulator({
blockchainConfig: this.config.blockchainConfig,
logger: this.logger
});
simulator.run(options);
}
generateTemplate(templateName, destinationFolder, name) {
this.context = [constants.contexts.templateGeneration];
let TemplateGenerator = require('./cmds/template_generator.js');
let templateGenerator = new TemplateGenerator(templateName);
templateGenerator.generate(destinationFolder, name);
}
run(options) {
let self = this;
self.context = options.context || [constants.contexts.run, constants.contexts.build];
let Dashboard = require('./dashboard/dashboard.js');
const Engine = require('./core/engine.js');
const engine = new Engine({
env: options.env,
client: options.client,
locale: options.locale,
isDev: this.isDev(options.env),
version: this.version,
embarkConfig: options.embarkConfig || 'embark.json',
logFile: options.logFile,
logLevel: options.logLevel,
context: self.context,
useDashboard: options.useDashboard
});
engine.init();
if (!options.useDashboard) {
engine.logger.info('========================'.bold.green);
engine.logger.info((__('Welcome to Embark') + ' ' + this.version).yellow.bold);
engine.logger.info('========================'.bold.green);
}
async.parallel([
function startDashboard(callback) {
if (!options.useDashboard) {
return callback();
}
let dashboard = new Dashboard({
events: engine.events,
logger: engine.logger,
plugins: engine.plugins,
version: self.version,
env: engine.env,
contractsConfig: engine.config.contractsConfig
});
dashboard.start(function () {
engine.logger.info(__('dashboard start'));
callback();
});
},
function (callback) {
let pluginList = engine.plugins.listPlugins();
if (pluginList.length > 0) {
engine.logger.info(__("loaded plugins") + ": " + pluginList.join(", "));
}
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.events.on('check:backOnline:Ethereum', function () {
engine.logger.info(__('Ethereum node detected') + '..');
engine.config.reloadConfig();
engine.events.request('deploy:contracts', function (err) {
if (err) {
return;
}
engine.logger.info(__('Deployment Done'));
});
});
engine.events.on('outputDone', function () {
engine.logger.warn(__("Note: The console uses ").green + (__("web3.js 1.0").underline + " ").cyan + " the reference docs for web3.js can be found at http://web3js.readthedocs.io/en/1.0/".green);
engine.logger.info((__("Looking for documentation? You can find it at") + " ").cyan + "http://embark.status.im/docs/".green.underline + ".".cyan);
engine.logger.info(__("Ready").underline);
engine.events.emit("status", __("Ready").green);
});
if (options.runWebserver) {
engine.startService("webServer", {
host: options.serverHost,
port: options.serverPort
});
}
engine.startService("fileWatcher");
callback();
}
], function (err, _result) {
if (err) {
engine.logger.error(err.message);
engine.logger.info(err.stack);
} else {
engine.events.emit('firstDeploymentDone');
}
});
}
build(options) {
this.context = options.context || [constants.contexts.build];
const Engine = require('./core/engine.js');
const engine = new Engine({
env: options.env,
client: options.client,
locale: options.locale,
isDev: this.isDev(options.env),
version: this.version,
embarkConfig: 'embark.json',
interceptLogs: false,
logFile: options.logFile,
logLevel: options.logLevel,
events: options.events,
logger: options.logger,
config: options.config,
plugins: options.plugins,
context: this.context
});
engine.init();
async.waterfall([
function startServices(callback) {
let pluginList = engine.plugins.listPlugins();
if (pluginList.length > 0) {
engine.logger.info(__("loaded plugins") + ": " + pluginList.join(", "));
}
engine.startService("libraryManager");
engine.startService("codeRunner");
engine.startService("web3");
if (!options.onlyCompile) {
engine.startService("pipeline");
}
engine.startService("deployment", {onlyCompile: options.onlyCompile});
engine.startService("storage");
engine.startService("codeGenerator");
callback();
},
function deploy(callback) {
engine.events.request('deploy:contracts', function (err) {
callback(err);
});
},
function waitForWriteFinish(callback) {
if (options.onlyCompile) {
engine.logger.info("Finished compiling".underline);
return callback(null, true);
}
engine.logger.info("Finished deploying".underline);
if (!engine.config.assetFiles || !Object.keys(engine.config.assetFiles).length) {
return callback();
}
engine.events.on('outputDone', (err) => {
engine.logger.info(__("finished building").underline);
callback(err, true);
});
}
], function (err, canExit) {
if (err) {
engine.logger.error(err.message);
engine.logger.debug(err.stack);
}
if (canExit || !engine.config.contractsConfig.afterDeploy || !engine.config.contractsConfig.afterDeploy.length) {
process.exit();
}
engine.logger.info(__('Waiting for after deploy to finish...'));
engine.logger.info(__('You can exit with CTRL+C when after deploy completes'));
});
}
graph(options) {
this.context = options.context || [constants.contexts.graph];
options.onlyCompile = true;
const Engine = require('./core/engine.js');
const engine = new Engine({
env: options.env,
isDev: this.isDev(options.env),
version: this.version,
embarkConfig: options.embarkConfig || 'embark.json',
logFile: options.logFile,
context: this.context
});
engine.init();
async.waterfall([
function (callback) {
let pluginList = engine.plugins.listPlugins();
if (pluginList.length > 0) {
engine.logger.info(__("loaded plugins") + ": " + pluginList.join(", "));
}
engine.startService("serviceMonitor");
engine.startService("libraryManager");
engine.startService("pipeline");
engine.startService("deployment", {onlyCompile: true});
engine.startService("web3");
engine.startService("codeGenerator");
engine.events.request('deploy:contracts', callback);
}
], (err) => {
if (err) {
engine.logger.error(err.message);
engine.logger.info(err.stack);
} else {
const GraphGenerator = require('./cmds/graph.js');
let graphGen = new GraphGenerator(engine);
graphGen.generate(options);
engine.logger.info(__("Done. %s generated", "./diagram.svg").underline);
}
process.exit();
});
}
reset() {
this.context = [constants.contexts.reset];
let resetCmd = require('./cmds/reset.js');
resetCmd();
}
upload(options) {
this.context = options.context || [constants.contexts.upload, constants.contexts.build];
const Engine = require('./core/engine.js');
const engine = new Engine({
env: options.env,
client: options.client,
locale: options.locale,
isDev: this.isDev(options.env),
version: this.version,
embarkConfig: 'embark.json',
interceptLogs: false,
logFile: options.logFile,
logLevel: options.logLevel,
events: options.events,
logger: options.logger,
config: options.config,
plugins: options.plugins,
context: this.context
});
engine.init();
let platform = engine.config.storageConfig.upload.provider;
let cmdPlugin;
async.waterfall([
function startServices(callback) {
engine.startService("serviceMonitor");
engine.startService("libraryManager");
engine.startService("codeRunner");
engine.startService("web3");
engine.startService("pipeline");
engine.startService("deployment");
engine.startService("storage");
engine.startService("codeGenerator");
callback();
},
function setupStoragePlugin(callback) {
let pluginList = engine.plugins.listPlugins();
if (pluginList.length > 0) {
engine.logger.info(__("loaded plugins") + ": " + pluginList.join(", "));
}
// check use has input existing storage plugin
let cmdPlugins = engine.plugins.getPluginsFor('uploadCmds');
if (cmdPlugins.length > 0) {
cmdPlugin = cmdPlugins.find((pluginCmd) => {
return pluginCmd.uploadCmds.some(uploadCmd => {
return uploadCmd.cmd === platform;
});
});
}
if (!cmdPlugin) {
return callback({message: __('platform "{{platform}}" is specified as the upload provider, however no plugins have registered an upload command for "{{platform}}".', {platform: platform})});
}
callback();
},
function deploy(callback) {
engine.events.on('outputDone', function () {
cmdPlugin.uploadCmds[0].cb()
.then((success) => {
callback(null, success);
})
.catch(callback);
});
engine.events.request('deploy:contracts', function (err) {
engine.logger.info(__("finished deploying").underline);
if (err) {
callback(err);
}
});
}
], function (err, _result) {
if (err) {
engine.logger.error(err.message);
engine.logger.debug(err.stack);
} else {
engine.logger.info((__("finished building DApp and deploying to") + " " + platform).underline);
}
// needed due to child processes
process.exit();
});
}
runTests(options) {
this.context = [constants.contexts.test];
let RunTests = require('./tests/run_tests.js');
RunTests.run(options);
}
}
module.exports = Embark;

View File

@ -1,25 +1,34 @@
const Web3 = require('web3');
const async = require('async');
const Provider = require('./provider.js');
const BlockchainProcessLauncher = require('../processes/blockchainProcessLauncher');
const utils = require('../utils/utils');
const constants = require('../constants');
const utils = require('../../utils/utils');
const constants = require('../../constants');
const embarkJsUtils = require('embarkjs').Utils;
const WEB3_READY = 'web3Ready';
class Blockchain {
constructor(options) {
// TODO: consider another name, this is the blockchain connector
class BlockchainConnector {
constructor(embark, options) {
const self = this;
this.plugins = options.plugins;
this.logger = options.logger;
this.events = options.events;
this.contractsConfig = options.contractsConfig;
this.blockchainConfig = options.blockchainConfig;
this.logger = embark.logger;
this.events = embark.events;
this.contractsConfig = embark.config.contractsConfig;
this.blockchainConfig = embark.config.blockchainConfig;
this.web3 = options.web3;
this.locale = options.locale;
this.isDev = options.isDev;
this.web3Endpoint = '';
this.isWeb3Ready = false;
this.web3StartedInProcess = false;
this.deployers = {};
self.events.setCommandHandler("blockchain:web3:isReady", (cb) => {
cb(self.isWeb3Ready);
});
self.events.setCommandHandler("blockchain:object", (cb) => {
cb(self);
});
if (!this.web3) {
this.initWeb3();
@ -29,8 +38,11 @@ class Blockchain {
this.registerServiceCheck();
this.registerRequests();
this.registerWeb3Object();
this.registerEvents();
this.subscribeToPendingTransactions();
}
//initWeb3() {
initWeb3(cb) {
if (!cb) {
cb = function(){};
@ -39,18 +51,19 @@ class Blockchain {
this.events.emit(WEB3_READY);
return cb();
}
const self = this;
this.web3 = new Web3();
if (this.contractsConfig.deployment.type !== "rpc" && this.contractsConfig.deployment.type !== "ws") {
const message = __("contracts config error: unknown deployment type %s", this.contractsConfig.deployment.type);
this.logger.error(message);
return cb(message);
}
const protocol = (this.contractsConfig.deployment.type === "rpc") ? this.contractsConfig.deployment.protocol : 'ws';
let provider;
this.web3Endpoint = utils.buildUrl(protocol, this.contractsConfig.deployment.host, this.contractsConfig.deployment.port);//`${protocol}://${this.contractsConfig.deployment.host}:${this.contractsConfig.deployment.port}`;
const providerOptions = {
web3: this.web3,
accountsConfig: this.contractsConfig.deployment.accounts,
@ -60,39 +73,44 @@ class Blockchain {
type: this.contractsConfig.deployment.type,
web3Endpoint: self.web3Endpoint
};
provider = new Provider(providerOptions);
this.provider = new Provider(providerOptions);
async.waterfall([
function checkNode(next) {
self.assertNodeConnection(true, (err) => {
if (err && self.web3StartedInProcess) {
// Already started blockchain in another node, we really have a node problem
self.logger.error(__('Unable to start the blockchain process. Is Geth installed?').red);
return next(err);
}
if (!err) {
self.isWeb3Ready = true;
self.events.emit(WEB3_READY);
return next();
}
self.web3StartedInProcess = true;
self.startBlockchainNode(() => {
// Need to re-initialize web3 to connect to the new blockchain node
provider.stop();
self.initWeb3(cb);
});
self.events.request("processes:launch", "blockchain", () => {
self.provider.startWeb3Provider(() => {
this.web3.eth.net.getId()
.then(id => {
let networkId = self.blockchainConfig.networkId;
if (!networkId && constants.blockchain.networkIds[self.blockchainConfig.networkType]) {
networkId = constants.blockchain.networkIds[self.blockchainConfig.networkType];
}
if (networkId && id.toString() !== networkId.toString()) {
self.logger.warn(__('Connected to a blockchain node on network {{realId}} while your config specifies {{configId}}', {realId: id, configId: networkId}));
self.logger.warn(__('Make sure you started the right blockchain node'));
}
})
.catch(console.error);
self.provider.fundAccounts(() => {
self.isWeb3Ready = true;
self.events.emit(WEB3_READY);
self.registerWeb3Object();
});
},
function startProvider(next) {
provider.startWeb3Provider(next);
},
function fundAccountsIfNeeded(next) {
self.isWeb3Ready = true;
provider.fundAccounts(next);
}
], (err) => {
self.registerWeb3Object();
cb(err);
});
});
}
registerEvents() {
const self = this;
self.events.on('check:wentOffline:Ethereum', () => {
self.logger.warn('Ethereum went offline: stopping web3 provider...');
self.provider.stop();
// once the node goes back online, we can restart the provider
self.events.once('check:backOnline:Ethereum', () => {
self.logger.warn('Ethereum back online: starting web3 provider...');
self.provider.startWeb3Provider(() => {
self.logger.warn('web3 provider restarted after ethereum node came back online');
});
});
});
}
@ -106,23 +124,6 @@ class Blockchain {
});
}
startBlockchainNode(callback) {
const self = this;
let blockchainProcess = new BlockchainProcessLauncher({
events: self.events,
logger: self.logger,
normalizeInput: utils.normalizeInput,
blockchainConfig: self.blockchainConfig,
locale: self.locale,
isDev: self.isDev
});
blockchainProcess.startBlockchainNode();
self.events.once(constants.blockchain.blockchainReady, () => {
callback();
});
}
registerServiceCheck() {
const self = this;
const NO_NODE = 'noNode';
@ -130,26 +131,29 @@ class Blockchain {
this.events.request("services:register", 'Ethereum', function (cb) {
async.waterfall([
function checkNodeConnection(next) {
self.assertNodeConnection(true, (err) => {
if (err) {
return next(NO_NODE, {name: "No Blockchain node found", status: 'off'});
}
next();
});
if (!self.web3.currentProvider) {
return next(NO_NODE, {name: "No Blockchain node found", status: 'off'});
}
next();
},
function checkVersion(next) {
// TODO: web3_clientVersion method is currently not implemented in web3.js 1.0
self.web3._requestManager.send({method: 'web3_clientVersion', params: []}, (err, version) => {
if (err) {
return next(null, {name: "Ethereum node (version unknown)", status: 'on'});
self.isWeb3Ready = false;
return next(null, {name: "Ethereum node not found", status: 'off'});
}
if (version.indexOf("/") < 0) {
self.events.emit(WEB3_READY);
self.isWeb3Ready = true;
return next(null, {name: version, status: 'on'});
}
let nodeName = version.split("/")[0];
let versionNumber = version.split("/")[1].split("-")[0];
let name = nodeName + " " + versionNumber + " (Ethereum)";
self.events.emit(WEB3_READY);
self.isWeb3Ready = true;
return next(null, {name: name, status: 'on'});
});
}
@ -166,7 +170,7 @@ class Blockchain {
const self = this;
this.events.setCommandHandler("blockchain:defaultAccount:get", function(cb) {
cb(self.defaultAccount);
cb(self.defaultAccount());
});
this.events.setCommandHandler("blockchain:defaultAccount:set", function(account, cb) {
@ -182,6 +186,9 @@ class Blockchain {
self.getGasPrice(cb);
});
this.events.setCommandHandler("blockchain:contract:create", function(params, cb) {
cb(self.ContractObject(params));
});
}
defaultAccount() {
@ -205,11 +212,14 @@ class Blockchain {
}
getGasPrice(cb) {
this.web3.eth.getGasPrice(cb);
const self = this;
this.onReady(() => {
self.web3.eth.getGasPrice(cb);
});
}
ContractObject(params) {
return new this.web3.eth.Contract(params.abi);
return new this.web3.eth.Contract(params.abi, params.address);
}
deployContractObject(contractObject, params) {
@ -223,55 +233,34 @@ class Blockchain {
}
deployContractFromObject(deployContractObject, params, cb) {
deployContractObject.send({
from: params.from, gas: params.gas, gasPrice: params.gasPrice
}).on('receipt', function(receipt) {
if (receipt.contractAddress !== undefined) {
cb(null, receipt);
}
}).on('error', cb);
}
let deployer = this.deployers[deployContractObject._deployData];
assertNodeConnection(noLogs, cb) {
if (typeof noLogs === 'function') {
cb = noLogs;
noLogs = false;
if (!deployer) {
deployer = {running: false, requested: []};
this.deployers[deployContractObject._deployData] = deployer;
}
const NO_NODE_ERROR = Error("error connecting to blockchain node");
const self = this;
async.waterfall([
function checkInstance(next) {
if (!self.web3) {
return next(Error("no web3 instance found"));
}
next();
},
function checkProvider(next) {
if (self.web3.currentProvider === undefined) {
return next(NO_NODE_ERROR);
}
next();
},
function pingEndpoint(next) {
if (!self.contractsConfig || !self.contractsConfig.deployment || !self.contractsConfig.deployment.host) {
return next();
}
const {host, port, type, protocol} = self.contractsConfig.deployment;
utils.pingEndpoint(host, port, type, protocol, self.blockchainConfig.wsOrigins.split(',')[0], next);
if (deployer.running) {
deployer.requested.push({deployContractObject, params, cb});
return;
}
deployer.running = true;
embarkJsUtils.secureSend(this.web3, deployContractObject, {
from: params.from, gas: params.gas, gasPrice: params.gasPrice
}, true, (error, receipt) => {
deployer.running = false;
if (deployer.requested.length > 0) {
let request = deployer.requested.shift();
this.deployContractFromObject(request.deployContractObject, request.params, request.cb);
}
], function (err) {
if (!noLogs && err === NO_NODE_ERROR) {
self.logger.error(("Couldn't connect to an Ethereum node are you sure it's on?").red);
self.logger.info("make sure you have an Ethereum node or simulator running. e.g 'embark blockchain'".magenta);
}
cb(err);
cb(error, receipt);
});
}
determineDefaultAccount(cb) {
const self = this;
self.getAccounts(function (err, accounts) {
self.getAccounts(function(err, accounts) {
if (err) {
self.logger.error(err);
return cb(new Error(err));
@ -286,9 +275,23 @@ class Blockchain {
registerWeb3Object() {
// doesn't feel quite right, should be a cmd or plugin method
// can just be a command without a callback
this.events.emit("runcode:register", "web3", this.web3);
this.events.emit("runcode:register", "web3", this.web3, false);
}
subscribeToPendingTransactions() {
const self = this;
this.onReady(() => {
if (self.logsSubscription) {
self.logsSubscription.unsubscribe();
}
self.logsSubscription = self.web3.eth
.subscribe('newBlockHeaders', () => {})
.on("data", function (blockHeader) {
self.events.emit('block:header', blockHeader);
});
});
}
}
module.exports = Blockchain;
module.exports = BlockchainConnector;

View File

@ -0,0 +1,84 @@
const async = require('async');
const AccountParser = require('../../utils/accountParser');
const fundAccount = require('./fundAccount');
class Provider {
constructor(options) {
this.web3 = options.web3;
this.accountsConfig = options.accountsConfig;
this.blockchainConfig = options.blockchainConfig;
this.type = options.type;
this.web3Endpoint = options.web3Endpoint;
this.logger = options.logger;
this.isDev = options.isDev;
}
startWeb3Provider(callback) {
const self = this;
if (this.type === 'rpc') {
self.provider = new this.web3.providers.HttpProvider(self.web3Endpoint);
} else if (this.type === 'ws') {
self.provider = new this.web3.providers.WebsocketProvider(self.web3Endpoint, {headers: {Origin: "embark"}});
self.provider.on('error', e => self.logger.error('Websocket Error', e));
self.provider.on('end', e => self.logger.error('Websocket connection ended', e));
} else {
return callback(__("contracts config error: unknown deployment type %s", this.type));
}
self.web3.setProvider(self.provider);
self.accounts = AccountParser.parseAccountsConfig(self.accountsConfig, self.web3, self.logger);
self.addresses = [];
if (!self.accounts.length) {
return callback();
}
self.accounts.forEach(account => {
self.addresses.push(account.address);
self.web3.eth.accounts.wallet.add(account);
});
self.web3.eth.defaultAccount = self.addresses[0];
const realSend = self.provider.send.bind(self.provider);
self.provider.send = function (payload, cb) {
if (payload.method === 'eth_accounts') {
return realSend(payload, function (err, result) {
if (err) {
return cb(err);
}
result.result = result.result.concat(self.addresses);
cb(null, result);
});
}
realSend(payload, cb);
};
callback();
}
stop() {
if (this.provider && this.provider.removeAllListeners) {
this.provider.removeAllListeners('connect');
this.provider.removeAllListeners('error');
this.provider.removeAllListeners('end');
this.provider.removeAllListeners('data');
this.provider.responseCallbacks = {};
this.provider = null;
}
}
fundAccounts(callback) {
const self = this;
if (!self.accounts.length) {
return callback();
}
if (!self.isDev) {
return callback();
}
async.eachLimit(self.accounts, 1, (account, eachCb) => {
fundAccount(self.web3, account.address, account.hexBalance, eachCb);
}, callback);
}
}
module.exports = Provider;

View File

@ -1,9 +1,9 @@
const async = require('async');
const child_process = require('child_process');
const _ = require('underscore');
const fs = require('../../core/fs.js');
const constants = require('../../constants.json');
const utils = require('../../utils/utils.js');
const GethCommands = require('./geth_commands.js');
const DevFunds = require('./dev_funds.js');
@ -17,12 +17,14 @@ var Blockchain = function(options) {
this.client = options.client;
this.isDev = options.isDev;
this.onReadyCallback = options.onReadyCallback || (() => {});
this.onExitCallback = options.onExitCallback;
this.proxyIpc = null;
if ((this.blockchainConfig === {} || JSON.stringify(this.blockchainConfig) === '{"enabled":true}') && this.env !== 'development') {
console.log("===> " + __("warning: running default config on a non-development environment"));
}
let defaultWsApi = ['eth', 'web3', 'net', 'shh', 'debug'];
let defaultWsApi = ['eth', 'web3', 'net', 'shh', 'debug', 'pubsub'];
if (this.isDev) {
defaultWsApi.push('personal');
}
@ -57,13 +59,11 @@ var Blockchain = function(options) {
verbosity: this.blockchainConfig.verbosity
};
this.setupProxy();
if (this.blockchainConfig === {} || JSON.stringify(this.blockchainConfig) === '{"enabled":true}') {
this.config.account = {};
this.config.account.password = fs.embarkPath("templates/boilerplate/config/development/password");
this.config.genesisBlock = fs.embarkPath("templates/boilerplate/config/development/genesis.json");
this.config.datadir = fs.embarkPath(".embark/development/datadir");
this.config.datadir = fs.dappPath(".embark/development/datadir");
}
const spaceMessage = 'The path for %s in blockchain config contains spaces, please remove them';
@ -79,28 +79,47 @@ var Blockchain = function(options) {
console.error(__(spaceMessage, 'genesisBlock'));
process.exit();
}
this.initProxy();
this.client = new options.client({config: this.config, env: this.env, isDev: this.isDev});
};
Blockchain.prototype.setupProxy = function() {
Blockchain.prototype.initProxy = function() {
this.config.proxy = true;
if (this.blockchainConfig.proxy === false) {
this.config.proxy = false;
return;
}
const proxy = require('../../core/proxy');
const Ipc = require('../../core/ipc');
let ipcObject = new Ipc({ipcRole: 'client'});
proxy.serve(ipcObject, this.config.rpcHost, this.config.rpcPort, false);
proxy.serve(ipcObject, this.config.wsHost, this.config.wsPort, true);
this.config.rpcPort += constants.blockchain.servicePortOnProxy;
this.config.wsPort += constants.blockchain.servicePortOnProxy;
};
Blockchain.prototype.setupProxy = async function() {
const proxy = require('./proxy');
const Ipc = require('../../core/ipc');
if(!this.proxyIpc) this.proxyIpc = new Ipc({ipcRole: 'client'});
let wsProxy;
if(this.config.wsRPC) {
wsProxy = proxy.serve(this.proxyIpc, this.config.wsHost, this.config.wsPort, true, this.config.wsOrigins);
}
[this.rpcProxy, this.wsProxy] = await Promise.all([
proxy.serve(this.proxyIpc, this.config.rpcHost, this.config.rpcPort, false),
wsProxy
]);
};
Blockchain.prototype.shutdownProxy = function() {
if (!this.config.proxy) {
return;
}
if(this.rpcProxy) this.rpcProxy.close();
if(this.wsProxy) this.wsProxy.close();
};
Blockchain.prototype.runCommand = function(cmd, options, callback) {
console.log(__("running: %s", cmd.underline).green);
if (this.blockchainConfig.silent) {
@ -116,7 +135,6 @@ Blockchain.prototype.run = function() {
console.log(__("Embark Blockchain Using: %s", this.client.name.underline).magenta);
console.log("===============================================================================".magenta);
console.log("===============================================================================".magenta);
this.checkPathLength();
let address = '';
async.waterfall([
@ -145,15 +163,14 @@ Blockchain.prototype.run = function() {
}
], function (err, cmd, args) {
if (err) {
console.error(err);
console.error(err.message);
return;
}
args = _.compact(args);
args = utils.compact(args);
let full_cmd = cmd + " " + args.join(' ');
console.log(__("running: %s", full_cmd.underline).green);
self.child = child_process.spawn(cmd, args, {cwd: process.cwd()});
self.child.on('error', (err) => {
err = err.toString();
console.error('Blockchain error: ', err);
@ -164,26 +181,56 @@ Blockchain.prototype.run = function() {
}
});
self.child.stdout.on('data', (data) => {
console.log(`Geth error: ${data}`);
console.error(`Geth error: ${data}`);
});
let httpReady = false;
let wsReady = !self.config.wsRPC;
// Geth logs appear in stderr somehow
self.child.stderr.on('data', (data) => {
self.child.stderr.on('data', async (data) => {
data = data.toString();
if (self.onReadyCallback && !self.readyCalled && data.indexOf('WebSocket endpoint opened') > -1) {
if (data.indexOf('HTTP endpoint opened') > -1) {
httpReady = true;
}
if (data.indexOf('WebSocket endpoint opened') > -1) {
wsReady = true;
}
if (!self.readyCalled && wsReady && httpReady) {
self.readyCalled = true;
if (self.isDev) {
self.createFundAndUnlockAccounts((err) => {
// TODO: this is never called!
if(err) console.error('Error creating, unlocking, and funding accounts', err);
});
}
self.readyCalled = true;
self.onReadyCallback();
if (self.config.proxy) {
await self.setupProxy();
}
self.readyCallback();
}
console.log('Geth: ' + data);
});
self.child.on('exit', (code) => {
let strCode;
if (code) {
console.error('Geth exited with error code ' + code);
strCode = ' with error code ' + code;
} else {
strCode = ' with no error code (manually killed?)';
}
console.error('Geth exited' + strCode);
if(self.onExitCallback){
self.onExitCallback();
}
});
self.child.on('uncaughtException', (err) => {
console.error('Uncaught geth exception', err);
if(self.onExitCallback){
self.onExitCallback();
}
});
});
@ -197,7 +244,20 @@ Blockchain.prototype.createFundAndUnlockAccounts = function(cb) {
});
};
Blockchain.prototype.readyCallback = function() {
if (this.onReadyCallback) {
this.onReadyCallback();
}
if (this.config.mineWhenNeeded && !this.isDev) {
const GethMiner = require('./miner');
this.miner = new GethMiner({datadir: this.blockchainConfig.datadir});
}
};
Blockchain.prototype.kill = function() {
this.shutdownProxy();
if (this.child) {
this.child.kill();
}
@ -238,10 +298,6 @@ Blockchain.prototype.initChainAndGetAddress = function(callback) {
next(err);
});
},
function copy(next) {
// copy mining script
fs.copy(fs.embarkPath("js"), ".embark/" + self.env + "/js", {overwrite: true}, next);
},
function listAccounts(next) {
self.runCommand(self.client.listAccountsCommand(), {}, (err, stdout, _stderr) => {
if (err || stdout === undefined || stdout.match(/{(\w+)}/) === null || stdout.indexOf("Fatal") >= 0) {
@ -279,13 +335,13 @@ Blockchain.prototype.initChainAndGetAddress = function(callback) {
});
};
var BlockchainClient = function(blockchainConfig, client, env, isDev, onReadyCallback) {
var BlockchainClient = function(blockchainConfig, client, env, onReadyCallback, onExitCallback) {
const isDev = !!blockchainConfig.isDev;
// TODO add other clients at some point
if (client === 'geth') {
return new Blockchain({blockchainConfig, client: GethCommands, env, isDev, onReadyCallback});
} else {
throw new Error('unknown client');
return new Blockchain({blockchainConfig, client: GethCommands, env, isDev, onReadyCallback, onExitCallback});
}
throw new Error('unknown client');
};
module.exports = BlockchainClient;

View File

@ -1,6 +1,6 @@
const ProcessWrapper = require('../../process/processWrapper');
const ProcessWrapper = require('../../core/processes/processWrapper');
const BlockchainClient = require('./blockchain');
const i18n = require('../../i18n/i18n.js');
const i18n = require('../../core/i18n/i18n.js');
const constants = require('../../constants');
let blockchainProcess;
@ -20,8 +20,8 @@ class BlockchainProcess extends ProcessWrapper {
this.blockchainConfig,
this.client,
this.env,
this.isDev,
this.blockchainReady.bind(this)
this.blockchainReady.bind(this),
this.blockchainExit.bind(this)
);
this.blockchain.run();
@ -31,6 +31,11 @@ class BlockchainProcess extends ProcessWrapper {
blockchainProcess.send({result: constants.blockchain.blockchainReady});
}
blockchainExit() {
// tell our parent process that geth has exited
blockchainProcess.send({result: constants.blockchain.blockchainExit});
}
kill() {
this.blockchain.kill();
}

View File

@ -1,6 +1,6 @@
const ProcessLauncher = require('../process/processLauncher');
const utils = require('../utils/utils.js');
const constants = require('../constants');
const ProcessLauncher = require('../../core/processes/processLauncher');
const utils = require('../../utils/utils.js');
const constants = require('../../constants');
class BlockchainProcessLauncher {
@ -21,7 +21,7 @@ class BlockchainProcessLauncher {
this.logger.info(__('Starting Blockchain node in another process').cyan);
this.blockchainProcess = new ProcessLauncher({
modulePath: utils.joinPath(__dirname, '../cmds/blockchain/blockchainProcess.js'),
modulePath: utils.joinPath(__dirname, './blockchainProcess.js'),
logger: this.logger,
events: this.events,
silent: this.logger.logLevel !== 'trace',
@ -44,6 +44,14 @@ class BlockchainProcessLauncher {
this.events.emit(constants.blockchain.blockchainReady);
});
this.blockchainProcess.once('result', constants.blockchain.blockchainExit, () => {
// telle everyone that our blockchain process (ie geth) died
this.events.emit(constants.blockchain.blockchainExit);
// then kill off the blockchain process
this.blockchainProcess.kill();
});
this.events.on('exit', () => {
this.blockchainProcess.send('exit');
});

View File

@ -21,7 +21,7 @@ class GethCommands {
}
if (config.syncMode || config.syncmode) {
cmd.push("--syncmode=" + (config.syncMode || config.syncmode));
cmd.push("--syncmode=" + (config.syncmode || config.syncMode));
}
if (config.account && config.account.password) {
@ -63,6 +63,9 @@ class GethCommands {
}
newAccountCommand() {
if (!(this.config.account && this.config.account.password)){
console.warn(__('Your blockchain is missing a password and creating an account may fail. Please consider updating ').yellow + __('config/blockchain > account > password').cyan + __(' then re-run the command').yellow);
}
return this.geth_bin + " " + this.commonOptions().join(' ') + " account new ";
}
@ -221,13 +224,6 @@ class GethCommands {
}
callback(null, "");
},
function mineWhenNeeded(callback) {
if (config.mineWhenNeeded && !self.isDev) {
args.push("js .embark/" + self.env + "/js/mine.js");
return callback(null, "js .embark/" + self.env + "/js/mine.js");
}
callback(null, "");
},
function isDev(callback) {
if (self.isDev) {
args.push('--dev');

View File

@ -0,0 +1,89 @@
const async = require('async');
const utils = require('../../utils/utils.js');
const constants = require('../../constants');
const BlockchainProcessLauncher = require('./blockchainProcessLauncher');
class BlockchainModule {
constructor(embark, options) {
this.logger = embark.logger;
this.events = embark.events;
this.blockchainConfig = embark.config.blockchainConfig;
this.contractsConfig = embark.config.contractsConfig;
this.embark = embark;
this.locale = options.locale;
this.isDev = options.isDev;
this.ipc = options.ipc;
this.registerBlockchainProcess();
}
registerBlockchainProcess() {
const self = this;
this.events.request('processes:register', 'blockchain', (cb) => {
self.assertNodeConnection(true, (connected) => {
if (connected) return cb();
self.startBlockchainNode(cb);
});
});
if (!this.ipc.isServer()) return;
self.ipc.on('blockchain:node', (_message, cb) => {
cb(null, utils.buildUrlFromConfig(self.contractsConfig.deployment));
});
}
assertNodeConnection(noLogs, cb) {
if (typeof noLogs === 'function') {
cb = noLogs;
noLogs = false;
}
const self = this;
async.waterfall([
function checkWeb3State(next) {
self.events.request("blockchain:web3:isReady", (connected) => {
if (connected) {
return next(connected);
}
next();
});
},
function pingEndpoint(next) {
if (!self.contractsConfig || !self.contractsConfig.deployment || !self.contractsConfig.deployment.host) {
return next();
}
const {host, port, type, protocol} = self.contractsConfig.deployment;
utils.pingEndpoint(host, port, type, protocol, self.blockchainConfig.wsOrigins.split(',')[0], next);
}
], function (err) {
if (err === true || err === undefined) {
return cb(true);
}
return cb(false);
});
}
startBlockchainNode(callback) {
const self = this;
let blockchainProcess = new BlockchainProcessLauncher({
events: self.events,
logger: self.logger,
normalizeInput: utils.normalizeInput,
blockchainConfig: self.blockchainConfig,
locale: self.locale,
isDev: self.isDev
});
blockchainProcess.startBlockchainNode();
self.events.once(constants.blockchain.blockchainReady, () => {
callback();
});
self.events.once(constants.blockchain.blockchainExit, () => {
callback();
});
}
}
module.exports = BlockchainModule;

View File

@ -0,0 +1,325 @@
const async = require('async');
const NetcatClient = require('netcat/client');
//Constants
const minerStart = 'miner_start';
const minerStop = 'miner_stop';
const getHashRate = 'miner_getHashrate';
const getCoinbase = 'eth_coinbase';
const getBalance = 'eth_getBalance';
const newBlockFilter = 'eth_newBlockFilter';
const pendingBlockFilter = 'eth_newPendingTransactionFilter';
const getChanges = 'eth_getFilterChanges';
const getBlockCount = 'eth_getBlockTransactionCountByNumber';
class GethMiner {
constructor(options) {
const self = this;
// TODO: Find a way to load mining config from YML.
// In the meantime, just set an empty config object
this.config = {};
this.datadir = options.datadir;
self.interval = null;
self.callback = null;
self.started = null;
self.commandQueue = async.queue((task, callback) => {
self.callback = callback;
self.client.send(JSON.stringify({"jsonrpc": "2.0", "method": task.method, "params": task.params || [], "id": 1}));
}, 1);
const defaults = {
interval_ms: 15000,
initial_ether: 15000000000000000000,
mine_pending_txns: true,
mine_periodically: false,
mine_normally: false,
threads: 1
};
for (let key in defaults) {
if (this.config[key] === undefined) {
this.config[key] = defaults[key];
}
}
const isWin = process.platform === "win32";
let ipcPath;
if (isWin) {
ipcPath = '\\\\.\\pipe\\geth.ipc';
} else {
ipcPath = this.datadir + '/geth.ipc';
}
this.client = new NetcatClient();
this.client.unixSocket(ipcPath)
.enc('utf8')
.connect()
.on('data', (response) => {
try {
response = JSON.parse(response);
} catch (e) {
console.error(e);
return;
}
if (self.callback) {
self.callback(response.error, response.result);
}
});
if (this.config.mine_normally) {
this.startMiner();
return;
}
self.stopMiner(() => {
self.fundAccount(function (err) {
if (err) {
console.error(err);
return;
}
if (self.config.mine_periodically) self.start_periodic_mining();
if (self.config.mine_pending_txns) self.start_transaction_mining();
});
});
}
sendCommand(method, params, callback) {
if (typeof params === 'function') {
callback = params;
params = [];
}
if (!callback) {
callback = function () {
};
}
this.commandQueue.push({method, params: params || []}, callback);
}
startMiner(callback) {
if (this.started) {
if (callback) {
callback();
}
return;
}
this.started = true;
this.sendCommand(minerStart, callback);
}
stopMiner(callback) {
if (!this.started) {
if (callback) {
callback();
}
return;
}
this.started = false;
this.sendCommand(minerStop, callback);
}
getCoinbase(callback) {
if (this.coinbase) {
return callback(null, this.coinbase);
}
this.sendCommand(getCoinbase, (err, result) => {
if (err) {
return callback(err);
}
this.coinbase = result;
if (!this.coinbase) {
return callback('Failed getting coinbase account');
}
callback(null, this.coinbase);
});
}
accountFunded(callback) {
const self = this;
self.getCoinbase((err, coinbase) => {
if (err) {
return callback(err);
}
self.sendCommand(getBalance, [coinbase, 'latest'], (err, result) => {
if (err) {
return callback(err);
}
callback(null, parseInt(result, 16) >= self.config.initial_ether);
});
});
}
watchBlocks(filterCommand, callback, delay) {
const self = this;
self.sendCommand(filterCommand, (err, filterId) => {
if (err) {
return callback(err);
}
self.interval = setInterval(() => {
self.sendCommand(getChanges, [filterId], (err, changes) => {
if (err) {
console.error(err);
return;
}
if (!changes || !changes.length) {
return;
}
callback(null, changes);
});
}, delay || 1000);
});
}
mineUntilFunded(callback) {
const self = this;
this.startMiner();
self.watchBlocks(newBlockFilter, (err) => {
if (err) {
console.error(err);
return;
}
self.accountFunded((err, funded) => {
if (funded) {
clearTimeout(self.interval);
self.stopMiner();
callback();
}
});
});
}
fundAccount(callback) {
const self = this;
self.accountFunded((err, funded) => {
if (err) {
return callback(err);
}
if (funded) {
return callback();
}
console.log("== Funding account");
self.mineUntilFunded(callback);
});
}
pendingTransactions(callback) {
const self = this;
self.sendCommand(getBlockCount, ['pending'], (err, hexCount) => {
if (err) {
return callback(err);
}
callback(null, parseInt(hexCount, 16));
});
}
start_periodic_mining() {
const self = this;
const WAIT = 'wait';
let last_mined_ms = Date.now();
let timeout_set = false;
let next_block_in_ms;
self.startMiner();
self.watchBlocks(newBlockFilter, (err) => {
if (err) {
console.error(err);
return;
}
if (timeout_set) {
return;
}
async.waterfall([
function checkPendingTransactions(next) {
if (!self.config.mine_pending_txns) {
return next();
}
self.pendingTransactions((err, count) => {
if (err) {
return next(err);
}
if (count) {
return next(WAIT);
}
next();
});
},
function stopMiner(next) {
timeout_set = true;
const now = Date.now();
const ms_since_block = now - last_mined_ms;
last_mined_ms = now;
if (ms_since_block > self.config.interval_ms) {
next_block_in_ms = 0;
} else {
next_block_in_ms = (self.config.interval_ms - ms_since_block);
}
self.stopMiner();
console.log("== Looking for next block in " + next_block_in_ms + "ms");
next();
},
function startAfterTimeout(next) {
setTimeout(function () {
console.log("== Looking for next block");
timeout_set = false;
self.startMiner();
next();
}, next_block_in_ms);
}
], (err) => {
if (err === WAIT) {
return;
}
if (err) {
console.error(err);
}
});
});
}
start_transaction_mining() {
const self = this;
const pendingTrasactionsMessage = "== Pending transactions! Looking for next block...";
self.watchBlocks(pendingBlockFilter, (err) => {
if (err) {
console.error(err);
return;
}
self.sendCommand(getHashRate, (err, result) => {
if (result > 0) return;
console.log(pendingTrasactionsMessage);
self.startMiner();
});
}, 2000);
if (self.config.mine_periodically) return;
self.watchBlocks(newBlockFilter, (err) => {
if (err) {
console.error(err);
return;
}
self.pendingTransactions((err, count) => {
if (err) {
console.error(err);
return;
}
if (!count) {
console.log("== No transactions left. Stopping miner...");
self.stopMiner();
} else {
console.log(pendingTrasactionsMessage);
self.startMiner();
}
});
}, 2000);
}
}
module.exports = GethMiner;

View File

@ -0,0 +1,154 @@
const httpProxy = require('http-proxy');
const http = require('http');
const constants = require('../../constants.json');
const utils = require('../../utils/utils');
let commList = {};
let transactions = {};
let receipts = {};
const {canonicalHost, defaultHost} = require('../../utils/host');
const parseRequest = function (reqBody) {
let jsonO;
try {
jsonO = JSON.parse(reqBody);
} catch (e) {
return; // Request is not a json. Do nothing
}
if (jsonO.method === "eth_sendTransaction") {
commList[jsonO.id] = {
type: 'contract-log',
address: jsonO.params[0].to,
data: jsonO.params[0].data
};
} else if (jsonO.method === "eth_getTransactionReceipt") {
if (transactions[jsonO.params[0]]) {
transactions[jsonO.params[0]].receiptId = jsonO.id;
receipts[jsonO.id] = transactions[jsonO.params[0]].commListId;
}
}
};
const parseResponse = function (ipc, resBody) {
let jsonO;
try {
jsonO = JSON.parse(resBody);
} catch (e) {
return; // Response is not a json. Do nothing
}
if (commList[jsonO.id]) {
commList[jsonO.id].transactionHash = jsonO.result;
transactions[jsonO.result] = {commListId: jsonO.id};
} else if (receipts[jsonO.id] && jsonO.result && jsonO.result.blockNumber) {
// TODO find out why commList[receipts[jsonO.id]] is sometimes not defined
if (!commList[receipts[jsonO.id]]) {
commList[receipts[jsonO.id]] = {};
}
commList[receipts[jsonO.id]].blockNumber = jsonO.result.blockNumber;
commList[receipts[jsonO.id]].gasUsed = jsonO.result.gasUsed;
commList[receipts[jsonO.id]].status = jsonO.result.status;
if (ipc.connected && !ipc.connecting) {
ipc.request('log', commList[receipts[jsonO.id]]);
} else {
ipc.connecting = true;
ipc.connect(() => {
ipc.connecting = false;
});
}
delete transactions[commList[receipts[jsonO.id]].transactionHash];
delete commList[receipts[jsonO.id]];
delete receipts[jsonO.id];
}
};
exports.serve = async function (ipc, host, port, ws, origin) {
const _origin = origin ? origin.split(',')[0] : undefined;
const start = Date.now();
function awaitTarget() {
return new Promise(resolve => {
utils.pingEndpoint(
canonicalHost(host), port, ws ? 'ws': false, 'http', _origin, async (err) => {
if (!err || (Date.now() - start > 10000)) {
return resolve();
}
await utils.timer(250).then(awaitTarget).then(resolve);
}
);
});
}
await awaitTarget();
let proxy = httpProxy.createProxyServer({
target: {
host: canonicalHost(host),
port: port
},
ws: ws
});
proxy.on('error', function (e) {
console.error(__("Error forwarding requests to blockchain/simulator"), e.message);
});
proxy.on('proxyRes', (proxyRes) => {
let resBody = [];
proxyRes.on('data', (b) => resBody.push(b));
proxyRes.on('end', function () {
resBody = Buffer.concat(resBody).toString();
if (resBody) {
parseResponse(ipc, resBody);
}
});
});
let server = http.createServer((req, res) => {
let reqBody = [];
req.on('data', (b) => {
reqBody.push(b);
})
.on('end', () => {
reqBody = Buffer.concat(reqBody).toString();
if (reqBody) {
parseRequest(reqBody);
}
});
if (!ws) {
proxy.web(req, res);
}
});
if (ws) {
const WsParser = require('simples/lib/parsers/ws'); // npm install simples
server.on('upgrade', function (req, socket, head) {
proxy.ws(req, socket, head);
});
proxy.on('open', (proxySocket) => {
proxySocket.on('data', (data) => {
parseResponse(ipc, data.toString().substr(data.indexOf("{")));
});
});
proxy.on('proxyReqWs', (proxyReq, req, socket) => {
var parser = new WsParser(0, false);
socket.pipe(parser);
parser.on('frame', function (frame) {
parseRequest(frame.data);
});
});
}
const listenPort = port - constants.blockchain.servicePortOnProxy;
return new Promise(resolve => {
server.listen(listenPort, defaultHost, () => {
resolve(server);
});
});
};

View File

@ -0,0 +1,84 @@
const path = require('path');
const pkgUp = require('pkg-up');
let shelljs = require('shelljs');
let proxy = require('./proxy');
const Ipc = require('../../core/ipc');
const constants = require('../../constants.json');
const {defaultHost, dockerHostSwap} = require('../../utils/host');
const fs = require('../../core/fs.js');
class Simulator {
constructor(options) {
this.blockchainConfig = options.blockchainConfig;
this.logger = options.logger;
}
run(options) {
let cmds = [];
const ganache_main = require.resolve('ganache-cli', {paths: fs.embarkPath('node_modules')});
const ganache_json = pkgUp.sync(path.dirname(ganache_main));
const ganache_root = path.dirname(ganache_json);
const ganache_bin = require(ganache_json).bin;
let ganache;
if (typeof ganache_bin === 'string') {
ganache = path.join(ganache_root, ganache_bin);
} else {
ganache = path.join(ganache_root, ganache_bin['ganache-cli']);
}
let useProxy = this.blockchainConfig.proxy || false;
let host = (dockerHostSwap(options.host || this.blockchainConfig.rpcHost) || defaultHost);
let port = (options.port || this.blockchainConfig.rpcPort || 8545);
port = parseInt(port, 10) + (useProxy ? constants.blockchain.servicePortOnProxy : 0);
cmds.push("-p " + port);
cmds.push("-h " + host);
cmds.push("-a " + (options.numAccounts || 10));
cmds.push("-e " + (options.defaultBalance || 100));
cmds.push("-l " + (options.gasLimit || 8000000));
// adding mnemonic only if it is defined in the blockchainConfig or options
let simulatorMnemonic = this.blockchainConfig.simulatorMnemonic || options.simulatorMnemonic;
if (simulatorMnemonic) {
cmds.push("--mnemonic \"" + (simulatorMnemonic) +"\"");
}
// as ganache-cli documentation explains, the simulatorAccounts configuration overrides a mnemonic
let simulatorAccounts = this.blockchainConfig.simulatorAccounts || options.simulatorAccounts;
if (simulatorAccounts && simulatorAccounts.length > 0) {
let web3 = new (require('web3'))();
let AccountParser = require('../../utils/accountParser.js');
let parsedAccounts = AccountParser.parseAccountsConfig(simulatorAccounts, web3, this.logger);
parsedAccounts.forEach((account) => {
let cmd = '--account="' + account.privateKey + ','+account.hexBalance + '"';
cmds.push(cmd);
});
}
// adding blocktime only if it is defined in the blockchainConfig or options
let simulatorBlocktime = this.blockchainConfig.simulatorBlocktime || options.simulatorBlocktime;
if (simulatorBlocktime) {
cmds.push("-b \"" + (simulatorBlocktime) +"\"");
}
// Setting up network id for simulator from blockchainConfig or options.
// Otherwise ganache-cli would make random network id.
let networkId = this.blockchainConfig.networkId || options.networkId;
if (networkId) { // Don't handle networkId=="0" because it is not a valid networkId for ganache-cli.
cmds.push("--networkId " + networkId);
}
const programName = 'ganache-cli';
const program = ganache;
console.log(`running: ${programName} ${cmds.join(' ')}`);
shelljs.exec(`${program} ${cmds.join(' ')}`, {async : true});
if(useProxy){
let ipcObject = new Ipc({ipcRole: 'client'});
proxy.serve(ipcObject, host, port, false);
}
}
}
module.exports = Simulator;

View File

@ -1,6 +1,7 @@
whenEnvIsLoaded(function(){
__mainContext.__loadManagerInstance.doFirst(function(done) {
<%- block %>
})
});
});
EmbarkJS.environment = "<%- environment %>";
});

View File

@ -0,0 +1 @@
__mainContext.<%- className %> = new EmbarkJS.Blockchain.Contract({abi: <%- abi %>, address: <%- contractAddress %>, code: '<%- contract.code %>', gasEstimates: <%- gasEstimates %>});

View File

@ -0,0 +1,3 @@
var __mainContext = __mainContext || (
this ? this : typeof self !== 'undefined' ? self : void 0
);

View File

@ -0,0 +1,3 @@
EmbarkJS.Blockchain.connect(<%- connectionList %>, {warnAboutMetamask: <%= warnAboutMetamask %>}, function(err) {
<%- done %>
});

View File

@ -1,10 +1,9 @@
let async = require('async');
let fs = require('../core/fs.js');
const utils = require('../utils/utils.js');
let fs = require('../../core/fs.js');
const utils = require('../../utils/utils.js');
require('ejs');
const Templates = {
utils: require('./code_templates/utils.js.ejs'),
vanilla_contract: require('./code_templates/vanilla-contract.js.ejs'),
embarkjs_contract: require('./code_templates/embarkjs-contract.js.ejs'),
exec_when_ready: require('./code_templates/exec-when-ready.js.ejs'),
@ -14,22 +13,28 @@ const Templates = {
define_web3_simple: require('./code_templates/define-web3-simple.js.ejs'),
web3_connector: require('./code_templates/web3-connector.js.ejs'),
do_when_loaded: require('./code_templates/do-when-loaded.js.ejs'),
exec_when_env_loaded: require('./code_templates/exec-when-env-loaded.js.ejs'),
embark_building_placeholder: require('./code_templates/embark-building-placeholder.html.ejs')
exec_when_env_loaded: require('./code_templates/exec-when-env-loaded.js.ejs')
};
class CodeGenerator {
constructor(options) {
this.blockchainConfig = options.blockchainConfig || {};
constructor(embark, options) {
this.blockchainConfig = embark.config.blockchainConfig || {};
this.rpcHost = this.blockchainConfig.rpcHost || '';
this.rpcPort = this.blockchainConfig.rpcPort || '';
this.contractsConfig = options.contractsConfig || {};
this.storageConfig = options.storageConfig || {};
this.communicationConfig = options.communicationConfig || {};
this.namesystemConfig = options.namesystemConfig || {};
this.contractsConfig = embark.config.contractsConfig || {};
this.storageConfig = embark.config.storageConfig || {};
this.communicationConfig = embark.config.communicationConfig || {};
this.namesystemConfig = embark.config.namesystemConfig || {};
this.env = options.env || 'development';
this.plugins = options.plugins;
this.events = options.events;
this.events = embark.events;
this.listenToCommands();
const self = this;
this.events.setCommandHandler("code-generator:embarkjs:build", (cb) => {
self.buildEmbarkJS(cb);
});
}
listenToCommands() {
@ -88,17 +93,9 @@ class CodeGenerator {
cb(self.generateContractCode(contract, gasLimit));
});
this.events.setCommandHandler('embark-building-placeholder', (cb) => {
self.buildPlaceholderPage(cb);
self.events.setCommandHandler('code-generator:embarkjs:provider-code', (cb) => {
cb(self.getEmbarkJsProviderCode());
});
}
generateContext() {
let result = "";
result += Templates.main_context();
result += Templates.load_manager();
return result;
}
generateProvider(isDeployment) {
@ -106,17 +103,14 @@ class CodeGenerator {
let result = "";
let providerPlugins;
// TODO: check contractsConfig for enabled
if (self.blockchainConfig === {} || self.blockchainConfig.enabled === false) {
return "";
}
result += Templates.utils();
result += Templates.main_context();
result += Templates.load_manager();
result += Templates.define_when_env_loaded();
if (self.blockchainConfig === {} || self.blockchainConfig.enabled === false) {
return result;
}
if (this.plugins) {
providerPlugins = this.plugins.getPluginsFor('clientWeb3Provider');
}
@ -128,8 +122,12 @@ class CodeGenerator {
} else {
let web3Load;
if (this.contractsConfig === {} || this.contractsConfig.enabled === false) {
return result;
}
if (isDeployment) {
let connection = "http://" + this.contractsConfig.deployment.host + ":" + this.contractsConfig.deployment.port;
let connection = utils.buildUrlFromConfig(this.contractsConfig.deployment);
web3Load = Templates.define_web3_simple({url: connection, done: 'done();'});
} else {
let connectionList = "[" + this.contractsConfig.dappConnection.map((x) => '"' + x + '"').join(',') + "]";
@ -137,7 +135,7 @@ class CodeGenerator {
web3Load = Templates.web3_connector({connectionList: connectionList, done: 'done(err);', warnAboutMetamask: isDev});
}
result += Templates.do_when_loaded({block: web3Load});
result += Templates.do_when_loaded({block: web3Load, environment: this.env});
}
return result;
@ -207,7 +205,6 @@ class CodeGenerator {
return result;
}
generateStorageInitialization(useEmbarkJS) {
if (!useEmbarkJS || this.storageConfig === {}) return "";
@ -283,37 +280,38 @@ class CodeGenerator {
buildEmbarkJS(cb) {
const self = this;
let embarkjsCode = fs.readFileSync(fs.embarkPath('js/embark.js')).toString();
let embarkjsCode = "import EmbarkJS from 'embarkjs';";
embarkjsCode += "\nexport default EmbarkJS;";
embarkjsCode += "\nglobal.EmbarkJS = EmbarkJS";
let code = "";
async.waterfall([
function getWeb3Location(next) {
self.events.request("version:get:web3", function(web3Version) {
if (web3Version === "1.0.0-beta") {
return next(null, fs.embarkPath("js/web3-1.0.min.js"));
} else {
self.events.request("version:getPackageLocation", "web3", web3Version, function(err, location) {
return next(null, fs.dappPath(location));
});
return next(null, require.resolve("web3", {paths: fs.embarkPath("node_modules")}));
}
self.events.request("version:getPackageLocation", "web3", web3Version, function(err, location) {
return next(null, fs.dappPath(location));
});
});
},
function getImports(web3Location, next) {
web3Location = web3Location.replace(/\\/g, '/'); // Import paths must always have forward slashes
code += "\nimport Web3 from '" + web3Location + "';\n";
code += `\nimport Web3 from '${web3Location}';\n`;
code += "\nimport web3 from 'Embark/web3';\n";
code += "\nimport IpfsApi from 'ipfs-api';\n";
next();
},
function getJSCode(next) {
code += "\n" + embarkjsCode + "\n";
let pluginsWithCode = self.plugins.getPluginsFor('embarkjsCode');
for (let plugin of pluginsWithCode) {
code += plugin.embarkjs_code.join('\n');
}
code += self.getEmbarkJsProviderCode();
code += self.generateCommunicationInitialization(true);
code += self.generateStorageInitialization(true);
code += self.generateNamesInitialization(true);
next();
},
function writeFile(next) {
@ -326,17 +324,19 @@ class CodeGenerator {
});
}
getEmbarkJsProviderCode() {
return this.plugins.getPluginsFor('embarkjsCode').reduce((code, plugin) => (
code += plugin.embarkjs_code.join('\n')
), '');
}
buildContractJS(contractName, contractJSON, cb) {
let contractCode = "";
contractCode += "import web3 from 'Embark/web3';\n";
contractCode += "import EmbarkJS from 'Embark/EmbarkJS';\n";
contractCode += "let " + contractName + "JSONConfig = " + JSON.stringify(contractJSON) + ";\n";
contractCode += "let " + contractName + " = new EmbarkJS.Contract(" + contractName + "JSONConfig);\n";
contractCode += "\n__embarkContext.execWhenReady(function() {\n";
contractCode += "\n" + contractName + ".setProvider(web3.currentProvider);\n";
contractCode += "\n" + contractName + ".options.from = web3.eth.defaultAccount;\n";
contractCode += "\n});\n";
contractCode += `let ${contractName}JSONConfig = ${JSON.stringify(contractJSON)};\n`;
contractCode += `${contractName}JSONConfig.web3 = web3;\n`;
contractCode += `let ${contractName} = new EmbarkJS.Blockchain.Contract(${contractName}JSONConfig);\n`;
contractCode += "export default " + contractName + ";\n";
cb(contractCode);
@ -350,38 +350,30 @@ class CodeGenerator {
function getWeb3Location(next) {
self.events.request("version:get:web3", function(web3Version) {
if (web3Version === "1.0.0-beta") {
return next(null, utils.joinPath(fs.embarkPath("js/web3-1.0.min.js")));
} else {
self.events.request("version:getPackageLocation", "web3", web3Version, function(err, location) {
return next(null, fs.dappPath(location));
});
return next(null, require.resolve("web3", {paths: fs.embarkPath("node_modules")}));
}
self.events.request("version:getPackageLocation", "web3", web3Version, function(err, location) {
return next(null, fs.dappPath(location));
});
});
},
function getImports(web3Location, next) {
web3Location = web3Location.replace(/\\/g, '/'); // Import paths must always have forward slashes
code += "\nimport Web3 from '" + web3Location + "';\n";
code += `\nimport Web3 from '${web3Location}';\n`;
code += "\nglobal.Web3 = Web3;\n";
code += "\n if (typeof web3 !== 'undefined') {";
code += "\n } else {";
code += "\n if (typeof web3 === 'undefined') {";
code += "\n var web3 = new Web3();\n";
code += "\n }";
code += "\nglobal.web3 = web3;\n";
let providerCode = self.generateProvider(false);
code += providerCode;
code += "\nglobal.__embarkContext = __mainContext.__loadManagerInstance;\n";
code += "\nwindow.web3 = web3;\n";
code += "\nexport default web3;\n";
next(null, code);
}
], cb);
}
buildPlaceholderPage(cb) {
let html = Templates.embark_building_placeholder({buildingMsg: __('Embark is building, please wait...')});
cb(html);
}
}
module.exports = CodeGenerator;

View File

@ -1,12 +1,20 @@
let async = require('../utils/async_extend.js');
let async = require('../../utils/async_extend.js');
class Compiler {
constructor(options) {
constructor(embark, options) {
const self = this;
this.plugins = options.plugins;
this.logger = options.logger;
this.events = embark.events;
this.logger = embark.logger;
this.disableOptimizations = options.disableOptimizations;
this.events.setCommandHandler("compiler:contracts", function(contractFiles, options, cb) {
self.compile_contracts(contractFiles, options, cb);
});
}
compile_contracts(contractFiles, cb) {
compile_contracts(contractFiles, options, cb) {
const self = this;
let available_compilers = {};
@ -21,6 +29,10 @@ class Compiler {
let compiledObject = {};
let compilerOptions = {
disableOptimizations: this.disableOptimizations || options.disableOptimizations
};
async.eachObject(available_compilers,
function (extension, compiler, callback) {
let matchingFiles = contractFiles.filter(function (file) {
@ -35,7 +47,7 @@ class Compiler {
if (!matchingFiles || !matchingFiles.length) {
return callback();
}
compiler.call(compiler, matchingFiles, function (err, compileResult) {
compiler.call(compiler, matchingFiles, compilerOptions, function (err, compileResult) {
Object.assign(compiledObject, compileResult);
callback(err, compileResult);
});

View File

@ -0,0 +1,117 @@
let utils = require('../../utils/utils');
const EmbarkJS = require('embarkjs');
const IpfsApi = require('ipfs-api');
const Web3 = require('web3');
class Console {
constructor(_embark, options) {
this.events = options.events;
this.plugins = options.plugins;
this.version = options.version;
this.logger = options.logger;
this.ipc = options.ipc;
this.config = options.config;
if (this.ipc.isServer()) {
this.ipc.on('console:executeCmd', this.executeCmd.bind(this));
}
this.events.setCommandHandler("console:executeCmd", this.executeCmd.bind(this));
this.registerEmbarkJs();
}
processEmbarkCmd (cmd) {
if (cmd === 'help' || cmd === __('help')) {
let helpText = [
__('Welcome to Embark') + ' ' + this.version,
'',
__('possible commands are:'),
'versions - ' + __('display versions in use for libraries and tools like web3 and solc'),
// TODO: only if the blockchain is actually active!
// will need to pass te current embark state here
'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'),
'EmbarkJS - ' + __('EmbarkJS static functions for Storage, Messages, Names, etc.'),
'quit - ' + __('to immediatly exit (alias: exit)'),
'',
__('The web3 object and the interfaces for the deployed contracts and their methods are also available')
];
return helpText.join('\n');
} else if (['quit', 'exit', 'sair', 'sortir', __('quit')].indexOf(cmd) >= 0) {
utils.exit();
}
return false;
}
executeCmd(cmd, callback) {
var pluginCmds = this.plugins.getPluginsProperty('console', 'console');
for (let pluginCmd of pluginCmds) {
let pluginResult = pluginCmd.call(this, cmd, {});
if(typeof pluginResult !== 'object'){
if (pluginResult !== false && pluginResult !== 'false' && pluginResult !== undefined) {
this.logger.warn("[DEPRECATED] In future versions of embark, we expect the console command to return an object " +
"having 2 functions: match and process. The documentation with example can be found here: https://embark.status.im/docs/plugin_reference.html#embark-registerConsoleCommand-callback-options");
return callback(null, pluginResult);
}
} else if (pluginResult.match()) {
return pluginResult.process(callback);
}
}
let output = this.processEmbarkCmd(cmd);
if (output) {
return callback(null, output);
}
try {
this.events.request('runcode:eval', cmd, callback);
}
catch (e) {
if (this.ipc.connected && this.ipc.isClient()) {
return this.ipc.request('console:executeCmd', cmd, callback);
}
callback(e);
}
}
registerEmbarkJs() {
this.events.emit('runcode:register', 'IpfsApi', IpfsApi, false);
this.events.emit('runcode:register', 'Web3', Web3, false);
this.events.emit('runcode:register', 'EmbarkJS', EmbarkJS, false);
EmbarkJS.Blockchain.done = true;
if (this.ipc.connected) {
return;
}
this.events.once('code-generator-ready', () => {
this.events.request('code-generator:embarkjs:provider-code', (code) => {
const func = () => {};
this.events.request('runcode:eval', code, func, true);
this.events.request('runcode:eval', this.getInitProviderCode(), func, true);
});
});
}
getInitProviderCode() {
const codeTypes = {
'communication': this.config.communicationConfig || {},
'names': this.config.namesystemConfig || {},
'storage': this.config.storageConfig || {}
};
return this.plugins.getPluginsFor('initConsoleCode').reduce((acc, plugin) => {
Object.keys(codeTypes).forEach(codeTypeName => {
(plugin.embarkjs_init_console_code[codeTypeName] || []).forEach(initCode => {
let [block, shouldInit] = initCode;
if (shouldInit.call(plugin, codeTypes[codeTypeName])) {
acc += block;
}
});
});
return acc;
}, '');
}
}
module.exports = Console;

View File

@ -1,92 +1,102 @@
const utils = require('../../utils/utils.js');
class ConsoleListener {
constructor(embark, options) {
this.logger = embark.logger;
this.ipc = options.ipc;
this.events = embark.events;
this.addressToContract = [];
this.contractsConfig = embark.config.contractsConfig;
this.contractsDeployed = false;
this._listenForLogRequests();
constructor(embark, options) {
this.logger = embark.logger;
this.ipc = options.ipc;
this.events = embark.events;
this.addressToContract = [];
this.contractsConfig = embark.config.contractsConfig;
this.contractsDeployed = false;
this.outputDone = false;
this._listenForLogRequests();
this.events.on('outputDone', () => {
this.outputDone = true;
});
this.events.on("contractsDeployed", () => {
this.contractsDeployed = true;
this._updateContractList();
});
}
this.events.on("contractsDeployed", () => {
this.contractsDeployed = true;
this._updateContractList();
});
}
_updateContractList(){
this.events.request("contracts:list", (_err, contractsList) => {
if(_err) {
this.logger.error(__("no contracts found"));
return;
}
contractsList.forEach(contract => {
if(!contract.deployedAddress) return;
let address = contract.deployedAddress.toLowerCase();
if(!this.addressToContract[address]){
let funcSignatures = {};
contract.abiDefinition
.filter(func => func.type == "function")
.map(func => {
const name = func.name +
'(' +
(func.inputs ? func.inputs.map(input => input.type).join(',') : '') +
')';
funcSignatures[utils.sha3(name).substring(0, 10)] = {
name,
abi: func,
functionName: func.name
};
});
_updateContractList() {
this.events.request("contracts:list", (_err, contractsList) => {
if (_err) {
this.logger.error(__("no contracts found"));
return;
}
contractsList.forEach(contract => {
if (!contract.deployedAddress) return;
this.addressToContract[address] = {
name: contract.className,
functions: funcSignatures
};
}
let address = contract.deployedAddress.toLowerCase();
if (!this.addressToContract[address]) {
let funcSignatures = {};
contract.abiDefinition
.filter(func => func.type === "function")
.map(func => {
const name = func.name +
'(' +
(func.inputs ? func.inputs.map(input => input.type).join(',') : '') +
')';
funcSignatures[utils.sha3(name).substring(0, 10)] = {
name,
abi: func,
functionName: func.name
};
});
});
}
_listenForLogRequests(){
if(this.ipc.ipcRole !== 'server') return;
this.ipc.on('log', (request) => {
if(request.type == 'contract-log'){
if(!this.contractsDeployed) return;
this.addressToContract[address] = {
name: contract.className,
functions: funcSignatures,
silent: contract.silent
};
}
});
});
}
let {address, data, transactionHash, blockNumber, gasUsed, status} = request;
if(!this.addressToContract[address]){
this._updateContractList();
}
if(!this.addressToContract[address]) return;
_listenForLogRequests() {
if (this.ipc.ipcRole !== 'server') return;
this.ipc.on('log', (request) => {
if (request.type === 'contract-log') {
if (!this.contractsDeployed) return;
let {address, data, transactionHash, blockNumber, gasUsed, status} = request;
const contract = this.addressToContract[address];
const name = this.addressToContract[address].name;
const func = this.addressToContract[address].functions[data.substring(0, 10)];
const functionName = func.functionName;
if (!contract) {
this._updateContractList();
return;
}
const decodedParameters = utils.decodeParams(func.abi.inputs, data.substring(10));
let paramString = "";
if(func.abi.inputs){
func.abi.inputs.forEach((input) => {
let quote = input.type.indexOf("int") == -1 ? '"' : '';
paramString += quote + decodedParameters[input.name] + quote + ", ";
});
paramString = paramString.substring(0, paramString.length - 2);
}
const {name, silent} = contract;
if (silent && !this.outputDone) {
return;
}
gasUsed = utils.hexToNumber(gasUsed);
blockNumber = utils.hexToNumber(blockNumber);
this.logger.info(`Blockchain>`.underline + ` ${name}.${functionName}(${paramString})`.bold + ` | ${transactionHash} | gas:${gasUsed} | blk:${blockNumber} | status:${status}`);
} else {
this.logger.info(JSON.stringify(request));
}
});
}
const func = contract.functions[data.substring(0, 10)];
const functionName = func.functionName;
const decodedParameters = utils.decodeParams(func.abi.inputs, data.substring(10));
let paramString = "";
if (func.abi.inputs) {
func.abi.inputs.forEach((input) => {
let quote = input.type.indexOf("int") === -1 ? '"' : '';
paramString += quote + decodedParameters[input.name] + quote + ", ";
});
paramString = paramString.substring(0, paramString.length - 2);
}
gasUsed = utils.hexToNumber(gasUsed);
blockNumber = utils.hexToNumber(blockNumber);
this.logger.info(`Blockchain>`.underline + ` ${name}.${functionName}(${paramString})`.bold + ` | ${transactionHash} | gas:${gasUsed} | blk:${blockNumber} | status:${status}`);
} else {
this.logger.info(JSON.stringify(request));
}
});
}
}
module.exports = ConsoleListener;

View File

@ -2,28 +2,35 @@ let toposort = require('toposort');
let async = require('async');
const cloneDeep = require('clone-deep');
let utils = require('../utils/utils.js');
let utils = require('../../utils/utils.js');
// TODO: create a contract object
class ContractsManager {
constructor(options) {
constructor(embark, options) {
const self = this;
this.contractFiles = options.contractFiles;
this.contractsConfig = cloneDeep(options.contractsConfig || {});
this.logger = embark.logger;
this.events = embark.events;
this.contracts = {};
this.logger = options.logger;
this.plugins = options.plugins;
this.contractDependencies = {};
this.gasLimit = options.gasLimit;
this.deployOnlyOnConfig = false;
this.events = options.events;
this.compileError = false;
this.compileOnceOnly = options.compileOnceOnly;
this.disableOptimizations = options.disableOptimizations;
self.events.setCommandHandler('contracts:list', (cb) => {
cb(self.compileError, self.listContracts());
});
self.events.setCommandHandler('contracts:all', (cb) => {
cb(self.compileError, self.contracts);
});
self.events.setCommandHandler('contracts:dependencies', (cb) => {
cb(self.compileError, self.contractDependencies);
});
self.events.setCommandHandler("contracts:contract", (contractName, cb) => {
cb(self.getContract(contractName));
});
@ -35,6 +42,11 @@ class ContractsManager {
});
});
self.events.setCommandHandler("contracts:reset:dependencies", (cb) => {
self.contractDependencies = {};
cb();
});
self.events.on("deploy:contract:error", (_contract) => {
self.events.emit('contractsState', self.contractsState());
});
@ -52,6 +64,9 @@ class ContractsManager {
build(done) {
let self = this;
self.contracts = {};
let compilerOptions = {disableOptimizations: this.disableOptimizations};
async.waterfall([
function loadContractFiles(callback) {
self.events.request("config:contractsFiles", (contractsFiles) => {
@ -67,11 +82,11 @@ class ContractsManager {
},
function compileContracts(callback) {
self.events.emit("status", __("Compiling..."));
if (process.env.isTest && self.compiledContracts && Object.keys(self.compiledContracts).length) {
if (self.compileOnceOnly && self.compiledContracts && Object.keys(self.compiledContracts).length) {
// Only compile once for tests
return callback();
}
self.events.request("compiler:contracts", self.contractFiles, function (err, compiledObject) {
self.events.request("compiler:contracts", self.contractsFiles, compilerOptions, function (err, compiledObject) {
self.compiledContracts = compiledObject;
callback(err);
});
@ -102,7 +117,7 @@ class ContractsManager {
contract.code = compiledContract.code;
contract.runtimeBytecode = compiledContract.runtimeBytecode;
contract.realRuntimeBytecode = (contract.realRuntimeBytecode || contract.runtimeBytecode);
contract.realRuntimeBytecode = (compiledContract.realRuntimeBytecode || compiledContract.runtimeBytecode);
contract.swarmHash = compiledContract.swarmHash;
contract.gasEstimates = compiledContract.gasEstimates;
contract.functionHashes = compiledContract.functionHashes;
@ -129,7 +144,12 @@ class ContractsManager {
}
if (contract.code === "") {
self.logger.info(__("assuming %s to be an interface", className));
const message = __("assuming %s to be an interface", className);
if (contract.silent) {
self.logger.trace(message);
} else {
self.logger.info(message);
}
contract.deploy = false;
}
}
@ -174,6 +194,7 @@ class ContractsManager {
contract.code = parentContract.code;
contract.runtimeBytecode = parentContract.runtimeBytecode;
contract.realRuntimeBytecode = (parentContract.realRuntimeBytecode || parentContract.runtimeBytecode);
contract.gasEstimates = parentContract.gasEstimates;
contract.functionHashes = parentContract.functionHashes;
contract.abiDefinition = parentContract.abiDefinition;
@ -212,7 +233,7 @@ class ContractsManager {
contract = self.contracts[className];
// look in code for dependencies
let libMatches = (contract.code.match(/\:(.*?)(?=_)/g) || []);
let libMatches = (contract.code.match(/:(.*?)(?=_)/g) || []);
for (let match of libMatches) {
self.contractDependencies[className] = self.contractDependencies[className] || [];
self.contractDependencies[className].push(match.substr(1));
@ -231,14 +252,14 @@ class ContractsManager {
for (let j = 0; j < ref.length; j++) {
let arg = ref[j];
if (arg[0] === "$") {
if (arg[0] === "$" && !arg.startsWith('$accounts')) {
self.contractDependencies[className] = self.contractDependencies[className] || [];
self.contractDependencies[className].push(arg.substr(1));
self.checkDependency(className, arg.substr(1));
}
if (Array.isArray(arg)) {
for (let sub_arg of arg) {
if (sub_arg[0] === "$") {
if (sub_arg[0] === "$" && !sub_arg.startsWith('$accounts')) {
self.contractDependencies[className] = self.contractDependencies[className] || [];
self.contractDependencies[className].push(sub_arg.substr(1));
self.checkDependency(className, sub_arg.substr(1));
@ -248,18 +269,31 @@ class ContractsManager {
}
// look in onDeploy for dependencies
if (contract.onDeploy === [] || contract.onDeploy === undefined) continue;
let regex = /\$\w+/g;
contract.onDeploy.map((cmd) => {
cmd.replace(regex, (match) => {
self.contractDependencies[className] = self.contractDependencies[className] || [];
self.contractDependencies[className].push(match.substr(1));
if (contract.onDeploy !== [] && contract.onDeploy !== undefined) {
let regex = /\$\w+/g;
contract.onDeploy.map((cmd) => {
if (cmd.indexOf('$accounts') > -1) {
return;
}
cmd.replace(regex, (match) => {
self.contractDependencies[className] = self.contractDependencies[className] || [];
self.contractDependencies[className].push(match.substr(1));
});
});
});
}
// Remove duplicates
if (self.contractDependencies[className]) {
const o = {};
self.contractDependencies[className].forEach(function (e) {
o[e] = true;
});
self.contractDependencies[className] = Object.keys(o);
}
}
callback();
}
], function (err, _result) {
], function (err) {
if (err) {
self.compileError = true;
self.events.emit("status", __("Compile/Build error"));
@ -337,6 +371,9 @@ class ContractsManager {
for (let className in this.contracts) {
let contract = this.contracts[className];
if (contract.silent) {
continue;
}
let contractData;

View File

@ -0,0 +1,347 @@
const SourceMap = require('./source_map');
class ContractSource {
constructor(file, path, body) {
let self = this;
this.file = file;
this.path = path;
this.body = body;
this.lineLengths = body.split("\n").map((line) => { return line.length; });
this.lineCount = this.lineLengths.length;
this.lineOffsets = this.lineLengths.reduce((sum, _elt, i) => {
sum[i] = (i == 0) ? 0 : self.lineLengths[i-1] + sum[i-1] + 1;
return sum;
}, []);
this.contracts = {};
}
sourceMapToLocations(sourceMap) {
var [offset, length, ..._] = sourceMap.split(":").map((val) => {
return parseInt(val, 10);
});
var locations = {};
for(let i = 0; i < this.lineCount; i++) {
if(this.lineOffsets[i+1] <= offset) continue;
locations.start = {line: i, column: offset - this.lineOffsets[i]};
break;
}
for(var i = locations.start.line; i < this.lineCount; i++) {
if(this.lineOffsets[i+1] <= offset + length) continue;
locations.end = {line: i, column: ((offset + length) - this.lineOffsets[i])};
break;
}
// Ensure we return an "end" as a safeguard if the marker ends up to be
// or surpass the offset for last character.
if(!locations.end) {
var lastLine = this.lineCount - 1;
locations.end = {line: lastLine, column: this.lineLengths[lastLine]};
}
// Istanbul likes lines to be 1-indexed, so we'll increment here before returning.
locations.start.line++;
locations.end.line++;
return locations;
}
parseSolcOutput(source, contracts) {
this.id = source.id;
this.ast = source.ast;
this.contractBytecode = {};
for(var contractName in contracts) {
this.contractBytecode[contractName] = {};
var contract = contracts[contractName];
var bytecodeMapping = this.contractBytecode[contractName];
var opcodes = contract.evm.deployedBytecode.opcodes.trim().split(' ');
var sourceMaps = contract.evm.deployedBytecode.sourceMap.split(';');
var bytecodeIdx = 0;
var pc = 0;
var instructions = 0;
var previousSourceMap = null;
do {
let sourceMap;
if(previousSourceMap === null) {
sourceMap = new SourceMap(sourceMaps[instructions]);
} else {
sourceMap = previousSourceMap.createRelativeTo(sourceMaps[instructions]);
}
var instruction = opcodes[bytecodeIdx];
var length = this._instructionLength(instruction);
bytecodeMapping[pc] = {
instruction: instruction,
sourceMap: sourceMap,
jump: sourceMap.jump,
seen: false
};
pc += length;
instructions++;
bytecodeIdx += (length > 1) ? 2 : 1;
previousSourceMap = sourceMap;
} while(bytecodeIdx < opcodes.length);
}
}
isInterface() {
return this.contractBytecode !== undefined &&
Object.values(this.contractBytecode).every((contractBytecode) => { return (Object.values(contractBytecode).length <= 1); });
}
/*eslint complexity: ["error", 39]*/
generateCodeCoverage(trace) {
if(!this.ast || !this.contractBytecode) throw new Error('Error generating coverage: solc output was not assigned');
let coverage = {
code: this.body.trim().split("\n"),
l: {},
path: this.path,
s: {},
b: {},
f: {},
fnMap: {},
statementMap: {},
branchMap: {}
};
var nodesRequiringVisiting = [this.ast];
var sourceMapToNodeType = {};
do {
let node = nodesRequiringVisiting.pop();
if(!node) continue;
let children = [];
let markLocations = [];
let location;
switch(node.nodeType) {
case 'Assignment':
case 'EventDefinition':
case 'ImportDirective':
case 'Literal':
case 'PlaceholderStatement':
case 'PragmaDirective':
case 'StructDefinition':
case 'VariableDeclaration':
// We don't need to do anything with these. Just carry on.
break;
case 'IfStatement': {
location = this.sourceMapToLocations(node.src);
let trueBranchLocation = this.sourceMapToLocations(node.trueBody.src);
let declarationSourceMap = new SourceMap(node.src).subtract(new SourceMap(node.trueBody.src));
let declarationLocation = this.sourceMapToLocations(declarationSourceMap.toString());
var falseBranchLocation;
if(node.falseBody) {
falseBranchLocation = this.sourceMapToLocations(node.falseBody.src);
} else {
falseBranchLocation = trueBranchLocation;
}
coverage.b[node.id] = [0,0];
coverage.branchMap[node.id] = {
type: 'if',
locations: [trueBranchLocation, falseBranchLocation],
line: location.start.line
};
markLocations = [declarationLocation];
children = [node.condition];
let trueExpression = (node.trueBody && node.trueBody.statements && node.trueBody.statements[0]) || node.trueBody;
if(trueExpression) {
children = children.concat(trueExpression);
trueExpression._parent = {type: 'b', id: node.id, idx: 0};
}
let falseExpression = (node.falseBody && node.falseBody.statements && node.falseBody.statements[0]) || node.falseBody;
if(falseExpression) {
children = children.concat(falseExpression);
falseExpression._parent = {type: 'b', id: node.id, idx: 1};
}
sourceMapToNodeType[node.src] = [{type: 'b', id: node.id, body: {loc: location}}];
break;
}
case 'EmitStatement': {
children = [node.eventCall];
break;
}
case 'BinaryOperation':
case 'ExpressionStatement':
case 'FunctionCall':
case 'Identifier':
case 'Return':
case 'UnaryOperation':
coverage.s[node.id] = 0;
location = this.sourceMapToLocations(node.src);
coverage.statementMap[node.id] = location;
if(!sourceMapToNodeType[node.src]) sourceMapToNodeType[node.src] = [];
sourceMapToNodeType[node.src].push({
type: 's',
id: node.id,
body: {loc: coverage.statementMap[node.id]},
parent: node._parent
});
markLocations = [location];
break;
case 'ContractDefinition':
case 'SourceUnit':
children = node.nodes;
break;
case 'ModifierDefinition':
case 'FunctionDefinition':
// Istanbul only wants the function definition, not the body, so we're
// going to do some fun math here.
var functionSourceMap = new SourceMap(node.src);
var functionParametersSourceMap = new SourceMap(node.parameters.src);
var functionDefinitionSourceMap = new SourceMap(
functionSourceMap.offset,
(functionParametersSourceMap.offset + functionParametersSourceMap.length) - functionSourceMap.offset
).toString();
var fnName = node.isConstructor ? "(constructor)" : node.name;
location = this.sourceMapToLocations(functionDefinitionSourceMap);
coverage.f[node.id] = 0;
coverage.fnMap[node.id] = {
name: fnName,
line: location.start.line,
loc: location
};
// Record function positions.
sourceMapToNodeType[node.src] = [{type: 'f', id: node.id, body: coverage.fnMap[node.id]}];
if(node.body) children = node.body.statements;
markLocations = [location];
break;
case 'ForStatement': {
// For statements will be a bit of a special case. We want to count the body
// iterations but we only want to count the for loop being hit once. Because
// of this, we cover the initialization on the node.
let sourceMap = new SourceMap(node.src);
let bodySourceMap = new SourceMap(node.body.src);
let forLoopDeclaration = sourceMap.subtract(bodySourceMap).toString();
let initializationLocation = this.sourceMapToLocations(node.initializationExpression.src);
location = this.sourceMapToLocations(forLoopDeclaration);
coverage.s[node.id] = 0;
coverage.statementMap[node.id] = location;
if(!sourceMapToNodeType[node.initializationExpression.src]) sourceMapToNodeType[node.initializationExpression.src] = [];
sourceMapToNodeType[node.initializationExpression.src].push({type: 's', id: node.id, body: {loc: location}});
children = node.body.statements;
markLocations = [initializationLocation];
break;
}
case 'VariableDeclarationStatement': {
location = this.sourceMapToLocations(node.src);
coverage.s[node.id] = 0;
coverage.statementMap[node.id] = location;
markLocations = [location];
if(!sourceMapToNodeType[node.src]) sourceMapToNodeType[node.src] = [];
sourceMapToNodeType[node.src].push({type: 's', id: node.id, body: {loc: location}, foo: 'bar'});
break;
}
default:
console.log(`Don't know how to handle node type ${node.nodeType}`);
break;
}
nodesRequiringVisiting = nodesRequiringVisiting.concat(children);
markLocations.forEach((location) => {
for(var i = location.start.line; i <= location.end.line; i++) {
coverage.l[i] = 0;
}
});
} while(nodesRequiringVisiting.length > 0);
var contractMatches = true;
for(var contractName in this.contractBytecode) {
var bytecode = this.contractBytecode[contractName];
// Try to match the contract to the bytecode. If it doesn't,
// then we bail.
contractMatches = trace.structLogs.every((step) => { return bytecode[step.pc]; });
if(!contractMatches) continue;
trace.structLogs.forEach((step) => {
step = bytecode[step.pc];
if(!step.sourceMap || step.sourceMap === '' || step.sourceMap === SourceMap.empty()) return;
let sourceMapString = step.sourceMap.toString(this.id);
var nodes = sourceMapToNodeType[sourceMapString];
if(!nodes) return;
nodes.forEach((node) => {
// Skip duplicate function reports by only reporting when there is a jump.
if(node.type == 'f' && step.jump) return;
if(node.type != 'b' && node.body && node.body.loc) {
for(var line = node.body.loc.start.line; line <= node.body.loc.end.line; line++) {
coverage.l[line]++;
}
}
if(node.type != 'b') coverage[node.type][node.id]++;
if(!node.parent) return;
switch(node.parent.type) {
case 'b':
coverage.b[node.parent.id][node.parent.idx]++;
break;
default:
// do nothing
}
});
});
}
return coverage;
}
_instructionLength(instruction) {
if(instruction.indexOf('PUSH') == -1) return 1;
return parseInt(instruction.match(/PUSH(\d+)/m)[1], 10) + 1;
}
}
module.exports = ContractSource;

View File

@ -0,0 +1,97 @@
const fs = require('fs');
const path = require('path');
const ContractSource = require('./contract_source');
class ContractSources {
constructor(files) {
this.files = {};
switch(Object.prototype.toString.call(files)) {
case '[object Object]':
Object.keys(files).forEach((file) => { this.addFile(file, files[file]); });
break;
case '[object String]':
// No 'break' statement here on purpose, as it shares
// the logic below.
files = [files];
// falls through
case '[object Array]':
files.forEach((file) => {
var content = fs.readFileSync(file).toString();
this.addFile(file, content);
});
break;
default:
throw new Error(`Don't know how to initialize with ${Object.prototype.toString.call(files)}`);
}
}
addFile(fullPath, contents) {
let basename = path.basename(fullPath);
if(this.files[basename]) return;
this.files[basename] = new ContractSource(basename, fullPath, contents);
}
toSolcInputs() {
var inputs = {};
for(var file in this.files) {
inputs[file] = {content: this.files[file].body};
}
return inputs;
}
parseSolcOutput(output) {
for(var file in output.contracts) {
var contractSource = this.files[path.basename(file)];
if(!contractSource) continue;
contractSource.parseSolcOutput(output.sources[file], output.contracts[file]);
}
}
generateCodeCoverage(trace) {
var coverageReport = {};
for(var file in this.files) {
if(this.files[file].isInterface()) continue;
coverageReport[file] = this.files[file].generateCodeCoverage(trace);
}
if(!this.coverageReport) {
this.coverageReport = coverageReport;
return this.coverageReport;
}
// We already have a previous coverage report, so we're merging results here.
Object.keys(coverageReport).forEach((file) => {
if(!this.coverageReport[file]) {
this.coverageReport[file] = coverageReport[file];
return;
}
// Increment counters for statements, functions and lines
['s', 'f', 'l'].forEach((countType) => {
Object.keys(coverageReport[file][countType]).forEach((id) => {
this.coverageReport[file][countType][id] += coverageReport[file][countType][id];
});
});
// Branch counts are tracked in a different manner so we'll do these now
Object.keys(coverageReport[file].b).forEach((id) => {
this.coverageReport[file].b[id][0] += coverageReport[file].b[id][0];
this.coverageReport[file].b[id][1] += coverageReport[file].b[id][1];
});
});
return this.coverageReport;
}
}
module.exports = ContractSources;

View File

@ -0,0 +1,71 @@
/*global web3*/
const fs = require('../../core/fs');
const ContractSources = require('./contract_sources');
// Set up the web3 extension
web3.extend({
property: 'debug',
methods: [{name: 'traceTransaction', call: 'debug_traceTransaction', params: 2}]
});
class CodeCoverage {
constructor(embark, _options) {
this.events = embark.events;
this.logger = embark.logger;
embark.events.on('contracts:compile:solc', this.compileSolc.bind(this));
embark.events.on('contracts:compiled:solc', this.compiledSolc.bind(this));
embark.events.on('contracts:run:solc', this.runSolc.bind(this));
embark.events.on('block:header', this.runSolc.bind(this));
// These events are emitted from a test-specific Embark instance, so we need to
// pull it in from global.
global.embark.events.on('tests:finished', this.updateCoverageReport.bind(this));
this.seenTransactions = {};
this.coverageReport = {};
this.contractSources = new ContractSources([]);
this.dotEmbarkPath = fs.dappPath('.embark');
this.coverageReportPath = fs.dappPath('.embark', 'coverage.json');
}
compileSolc(input) {
Object.keys(input.sources).forEach((file) => {
this.contractSources.addFile(file, input.sources[file].content);
});
}
compiledSolc(output) {
this.contractSources.parseSolcOutput(output);
}
updateCoverageReport(cb) {
fs.mkdirp(this.dotEmbarkPath, () => {
fs.writeFile(this.coverageReportPath, JSON.stringify(this.coverageReport), cb);
});
}
async runSolc(receipt) {
let block = await web3.eth.getBlock(receipt.number);
if(block.transactions.length == 0) return;
let requests = [];
for(let i in block.transactions) {
var txHash = block.transactions[i];
if(this.seenTransactions[txHash]) return;
this.seenTransactions[txHash] = true;
requests.push(web3.debug.traceTransaction(txHash, {}));
}
let traces = await Promise.all(requests);
for(let i in traces) {
this.coverageReport = this.contractSources.generateCodeCoverage(traces[i]);
}
}
}
module.exports = CodeCoverage;

View File

@ -0,0 +1,63 @@
const EmptySourceMap = {
createRelativeTo: function(sourceMapString) {
if(sourceMapString === '') return EmptySourceMap;
return new SourceMap(sourceMapString);
},
toString: function() {
return '';
}
};
class SourceMap {
constructor(sourceMapStringOrOffset, length, id, jump) {
if(typeof sourceMapStringOrOffset == 'string') {
let [offset, length, id, jump] = sourceMapStringOrOffset.split(":");
this.offset = parseInt(offset, 10);
this.length = parseInt(length, 10);
if(id) this.id = parseInt(id, 10);
this.jump = jump;
} else {
this.offset = sourceMapStringOrOffset;
this.length = length;
this.id = id;
this.jump = jump;
}
}
createRelativeTo(sourceMapString) {
if(!sourceMapString) return EmptySourceMap;
let [offset, length, id, jump] = sourceMapString.split(":");
offset = (offset) ? parseInt(offset, 10) : this.offset;
id = (id) ? parseInt(id, 10) : this.id;
length = parseInt(length, 10);
return new SourceMap(offset, length, id, jump);
}
subtract(sourceMap) {
return new SourceMap(this.offset, sourceMap.offset - this.offset, this.id, this.jump);
}
toString(defaultId) {
let parts = [this.offset, this.length];
if(this.id !== undefined && this.id != '') {
parts.push(this.id);
} else if(defaultId !== undefined) {
parts.push(defaultId);
}
return parts.join(':');
}
static empty() {
return EmptySourceMap;
}
}
module.exports = SourceMap;

View File

@ -1,16 +1,14 @@
let async = require('async');
//require("../utils/debug_util.js")(__filename, async);
let utils = require('../utils/utils.js');
let utils = require('../../utils/utils.js');
class ContractDeployer {
constructor(options) {
const self = this;
this.blockchain = options.blockchain;
this.logger = options.logger;
this.events = options.events;
this.plugins = options.plugins;
this.gasLimit = options.gasLimit;
self.events.setCommandHandler('deploy:contract', (contract, cb) => {
self.checkAndDeployContract(contract, null, cb);
@ -19,7 +17,7 @@ class ContractDeployer {
// TODO: determining the arguments could also be in a module since it's not
// part of ta 'normal' contract deployment
determineArguments(suppliedArgs, contract, callback) {
determineArguments(suppliedArgs, contract, accounts, callback) {
const self = this;
let args = suppliedArgs;
@ -36,20 +34,29 @@ class ContractDeployer {
}
}
function parseArg(arg, cb) {
const match = arg.match(/\$accounts\[([0-9]+)]/);
if (match) {
if (!accounts[match[1]]) {
return cb(__('No corresponding account at index %d', match[1]));
}
return cb(null, accounts[match[1]]);
}
let contractName = arg.substr(1);
self.events.request('contracts:contract', contractName, (referedContract) => {
// Because we're referring to a contract that is not being deployed (ie. an interface),
// we still need to provide a valid address so that the ABI checker won't fail.
cb(null, (referedContract.deployedAddress || '0x0000000000000000000000000000000000000000'));
});
}
async.map(args, (arg, nextEachCb) => {
if (arg[0] === "$") {
let contractName = arg.substr(1);
self.events.request('contracts:contract', contractName, (referedContract) => {
nextEachCb(null, referedContract.deployedAddress);
});
parseArg(arg, nextEachCb);
} else if (Array.isArray(arg)) {
async.map(arg, (sub_arg, nextSubEachCb) => {
if (sub_arg[0] === "$") {
let contractName = sub_arg.substr(1);
self.events.request('contracts:contract', contractName, (referedContract) => {
nextSubEachCb(null, referedContract.deployedAddress);
});
parseArg(sub_arg, nextSubEachCb);
} else {
nextSubEachCb(null, sub_arg);
}
@ -65,6 +72,8 @@ class ContractDeployer {
checkAndDeployContract(contract, params, callback) {
let self = this;
contract.error = false;
let accounts = [];
let deploymentAccount;
if (contract.deploy === false) {
self.events.emit("deploy:contract:undeployed", contract);
@ -72,8 +81,43 @@ class ContractDeployer {
}
async.waterfall([
function requestBlockchainConnector(callback) {
self.events.request("blockchain:object", (blockchain) => {
self.blockchain = blockchain;
callback();
});
},
// TODO: can potentially go to a beforeDeploy plugin
function getAccounts(next) {
deploymentAccount = self.blockchain.defaultAccount();
self.blockchain.getAccounts(function (err, _accounts) {
if (err) {
return next(new Error(err));
}
accounts = _accounts;
// applying deployer account configuration, if any
if (typeof contract.fromIndex === 'number') {
deploymentAccount = accounts[contract.fromIndex];
if (deploymentAccount === undefined) {
return next(__("error deploying") + " " + contract.className + ": " + __("no account found at index") + " " + contract.fromIndex + __(" check the config"));
}
}
if (typeof contract.from === 'string' && typeof contract.fromIndex !== 'undefined') {
self.logger.warn(__('Both "from" and "fromIndex" are defined for contract') + ' "' + contract.className + '". ' + __('Using "from" as deployer account.'));
}
if (typeof contract.from === 'string') {
deploymentAccount = contract.from;
}
deploymentAccount = deploymentAccount || accounts[0];
contract.deploymentAccount = deploymentAccount;
next();
});
},
function _determineArguments(next) {
self.determineArguments(params || contract.args, contract, (err, realArgs) => {
self.determineArguments(params || contract.args, contract, accounts, (err, realArgs) => {
if (err) {
return next(err);
}
@ -93,18 +137,17 @@ class ContractDeployer {
return next(e.message);
}
contract.deployedAddress = contract.address;
self.logger.info(contract.className.bold.cyan + __(" already deployed at ").green + contract.address.bold.cyan);
self.logFunction(contract)(contract.className.bold.cyan + __(" already deployed at ").green + contract.address.bold.cyan);
self.events.emit("deploy:contract:deployed", contract);
return next();
}
// TODO find a better way to do that
if (process.env.isTest) {
return self.deployContract(contract, next);
}
// TODO: this should be a plugin API instead, if not existing, it should by default deploy the contract
self.events.request("deploy:contract:shouldDeploy", contract, function(trackedContract) {
if (!trackedContract) {
self.plugins.emitAndRunActionsForEvent('deploy:contract:shouldDeploy', {contract: contract, shouldDeploy: true}, function(_err, params) {
let trackedContract = params.contract;
if (!params.shouldDeploy) {
return self.willNotDeployContract(contract, trackedContract, next);
}
if (!trackedContract.address) {
return self.deployContract(contract, next);
}
@ -120,56 +163,38 @@ class ContractDeployer {
], callback);
}
willNotDeployContract(contract, trackedContract, callback) {
contract.deploy = false;
this.events.emit("deploy:contract:undeployed", contract);
callback();
}
contractAlreadyDeployed(contract, trackedContract, callback) {
const self = this;
self.logger.info(contract.className.bold.cyan + __(" already deployed at ").green + trackedContract.address.bold.cyan);
this.logFunction(contract)(contract.className.bold.cyan + __(" already deployed at ").green + trackedContract.address.bold.cyan);
contract.deployedAddress = trackedContract.address;
self.events.emit("deploy:contract:deployed", contract);
// TODO: can be moved into a afterDeploy event
// just need to figure out the gasLimit coupling issue
self.events.request('code-generator:contract:vanilla', contract, contract._gasLimit, (contractCode) => {
self.events.request('runcode:eval', contractCode);
self.events.request('runcode:eval', contractCode, () => {}, true);
return callback();
});
}
logFunction(contract) {
return contract.silent ? this.logger.trace.bind(this.logger) : this.logger.info.bind(this.logger);
}
deployContract(contract, callback) {
let self = this;
let accounts = [];
let contractParams = (contract.realArgs || contract.args).slice();
let contractCode = contract.code;
let deploymentAccount = self.blockchain.defaultAccount();
let deployObject;
async.waterfall([
// TODO: can potentially go to a beforeDeploy plugin
function getAccounts(next) {
self.blockchain.getAccounts(function (err, _accounts) {
if (err) {
return next(new Error(err));
}
accounts = _accounts;
// applying deployer account configuration, if any
if (typeof contract.fromIndex == 'number') {
deploymentAccount = accounts[contract.fromIndex];
if (deploymentAccount === undefined) {
return next(__("error deploying") + " " + contract.className + ": " + __("no account found at index") + " " + contract.fromIndex + __(" check the config"));
}
}
if (typeof contract.from == 'string' && typeof contract.fromIndex != 'undefined') {
self.logger.warn(__('Both "from" and "fromIndex" are defined for contract') + ' "' + contract.className + '". ' + __('Using "from" as deployer account.'));
}
if (typeof contract.from == 'string') {
deploymentAccount = contract.from;
}
deploymentAccount = deploymentAccount || accounts[0];
next();
});
},
function doLinking(next) {
let contractCode = contract.code;
self.events.request('contracts:list', (_err, contracts) => {
for (let contractObj of contracts) {
let filename = contractObj.filename;
@ -191,21 +216,28 @@ class ContractDeployer {
}
contractCode = contractCode.replace(new RegExp(toReplace, "g"), deployedAddress);
}
// saving code changes back to contract object
// saving code changes back to the contract object
contract.code = contractCode;
self.events.request('contracts:setBytecode', contract.className, contractCode);
next();
});
},
function applyBeforeDeploy(next) {
self.plugins.emitAndRunActionsForEvent('deploy:contract:beforeDeploy', {contract: contract}, next);
self.plugins.emitAndRunActionsForEvent('deploy:contract:beforeDeploy', {contract: contract}, (_params) => {
next();
});
},
function getGasPriceForNetwork(next) {
self.events.request("blockchain:gasPrice", (gasPrice) => {
self.events.request("blockchain:gasPrice", (err, gasPrice) => {
if (err) {
return next(new Error(__("could not get the gas price")));
}
contract.gasPrice = contract.gasPrice || gasPrice;
next();
});
},
function createDeployObject(next) {
let contractCode = contract.code;
let contractObject = self.blockchain.ContractObject({abi: contract.abiDefinition});
try {
@ -213,10 +245,9 @@ class ContractDeployer {
deployObject = self.blockchain.deployContractObject(contractObject, {arguments: contractParams, data: dataCode});
} catch(e) {
if (e.message.indexOf('Invalid number of parameters for "undefined"') >= 0) {
return next(new Error(__("attempted to deploy %s without specifying parameters", contract.className)));
} else {
return next(new Error(e));
return next(new Error(__("attempted to deploy %s without specifying parameters", contract.className)) + ". " + __("check if there are any params defined for this contract in this environment in the contracts configuration file"));
}
return next(new Error(e));
}
next();
},
@ -233,28 +264,33 @@ class ContractDeployer {
next();
},
function deployTheContract(next) {
self.logger.info(__("deploying") + " " + contract.className.bold.cyan + " " + __("with").green + " " + contract.gas + " " + __("gas").green);
let estimatedCost = contract.gas * contract.gasPrice;
self.logFunction(contract)(__("deploying") + " " + contract.className.bold.cyan + " " + __("with").green + " " + contract.gas + " " + __("gas at the price of").green + " " + contract.gasPrice + " " + __("Wei, estimated cost:").green + " " + estimatedCost + " Wei".green);
self.blockchain.deployContractFromObject(deployObject, {
from: deploymentAccount,
from: contract.deploymentAccount,
gas: contract.gas,
gasPrice: contract.gasPrice
}, function(error, receipt) {
if (error) {
contract.error = error.message;
self.events.emit("deploy:contract:error", contract);
if (error.message && error.message.indexOf('replacement transaction underpriced') !== -1) {
self.logger.warn("replacement transaction underpriced: This warning typically means a transaction exactly like this one is still pending on the blockchain");
}
return next(new Error("error deploying =" + contract.className + "= due to error: " + error.message));
}
self.logger.info(contract.className.bold.cyan + " " + __("deployed at").green + " " + receipt.contractAddress.bold.cyan);
self.logFunction(contract)(contract.className.bold.cyan + " " + __("deployed at").green + " " + receipt.contractAddress.bold.cyan + " " + __("using").green + " " + receipt.gasUsed + " " + __("gas").green);
contract.deployedAddress = receipt.contractAddress;
contract.transactionHash = receipt.transactionHash;
receipt.className = contract.className;
self.events.emit("deploy:contract:receipt", receipt);
self.events.emit("deploy:contract:deployed", contract);
// TODO: can be moved into a afterDeploy event
// just need to figure out the gasLimit coupling issue
self.events.request('code-generator:contract:vanilla', contract, contract._gasLimit, (contractCode) => {
self.events.request('runcode:eval', contractCode);
self.events.request('runcode:eval', contractCode, () => {}, true);
self.plugins.runActionsForEvent('deploy:contract:deployed', {contract: contract}, () => {
return next(null, receipt);
});

View File

@ -0,0 +1,175 @@
let async = require('async');
const ContractDeployer = require('./contract_deployer.js');
const cloneDeep = require('clone-deep');
class DeployManager {
constructor(embark, options) {
const self = this;
this.config = embark.config;
this.logger = embark.logger;
this.blockchainConfig = this.config.blockchainConfig;
this.events = embark.events;
this.plugins = options.plugins;
this.blockchain = options.blockchain;
this.gasLimit = false;
this.fatalErrors = false;
this.deployOnlyOnConfig = false;
this.onlyCompile = options.onlyCompile !== undefined ? options.onlyCompile : false;
this.contractDeployer = new ContractDeployer({
logger: this.logger,
events: this.events,
plugins: this.plugins
});
this.events.setCommandHandler('deploy:setGasLimit', (gasLimit) => {
self.gasLimit = gasLimit;
});
this.events.setCommandHandler('deploy:contracts', (cb) => {
self.deployContracts(cb);
});
this.events.setCommandHandler('deploy:contracts:test', (cb) => {
self.fatalErrors = true;
self.deployOnlyOnConfig = true;
self.deployContracts(cb);
});
}
deployAll(done) {
let self = this;
self.events.request('contracts:dependencies', (err, contractDependencies) => {
self.events.request('contracts:list', (err, contracts) => {
if (err) {
return done(err);
}
self.logger.info(__("deploying contracts"));
async.waterfall([
function (next) {
self.plugins.emitAndRunActionsForEvent("deploy:beforeAll", next);
},
function () {
const contractDeploys = {};
const errors = [];
contracts.forEach(contract => {
function deploy(result, callback) {
if (typeof result === 'function') {
callback = result;
}
contract._gasLimit = self.gasLimit;
self.events.request('deploy:contract', contract, (err) => {
if (err) {
contract.error = err.message || err;
self.logger.error(err.message || err);
errors.push(err);
}
callback();
});
}
const className = contract.className;
if (!contractDependencies[className] || contractDependencies[className].length === 0) {
contractDeploys[className] = deploy;
return;
}
contractDeploys[className] = cloneDeep(contractDependencies[className]);
contractDeploys[className].push(deploy);
});
try {
async.auto(contractDeploys, function(_err, _results) {
if (errors.length) {
_err = __("Error deploying contracts. Please fix errors to continue.");
self.logger.error(_err);
return done(_err);
}
if (contracts.length === 0) {
self.logger.info(__("no contracts found"));
return done();
}
self.logger.info(__("finished deploying contracts"));
done(err);
});
} catch (e) {
self.logger.error(e.message || e);
done(__('Error deploying'));
}
}
]);
});
});
}
deployContracts(done) {
let self = this;
if (self.blockchainConfig === {} || self.blockchainConfig.enabled === false) {
self.logger.info(__("Blockchain component is disabled in the config").underline);
this.events.emit('blockchainDisabled', {});
return done();
}
async.waterfall([
function requestBlockchainConnector(callback) {
self.events.request("blockchain:object", (blockchain) => {
self.blockchain = blockchain;
callback();
});
},
function buildContracts(callback) {
self.events.request("contracts:build", self.deployOnlyOnConfig, (err) => {
callback(err);
});
},
// TODO: shouldn't be necessary
function checkCompileOnly(callback) {
if (self.onlyCompile) {
self.events.emit('contractsDeployed');
return done();
}
return callback();
},
// TODO: could be implemented as an event (beforeDeployAll)
function checkIsConnectedToBlockchain(callback) {
self.blockchain.onReady((err) => {
callback(err);
});
},
// TODO: this can be done on the fly or as part of the initialization
function determineDefaultAccount(callback) {
self.blockchain.determineDefaultAccount((err) => {
callback(err);
});
},
function deployAllContracts(callback) {
self.deployAll(function (err) {
if (!err) {
self.events.emit('contractsDeployed');
}
if (err && self.fatalErrors) {
return callback(err);
}
callback();
});
},
function runAfterDeploy(callback) {
self.plugins.emitAndRunActionsForEvent('contracts:deploy:afterAll', callback);
}
], function (err, _result) {
done(err);
});
}
}
module.exports = DeployManager;

View File

@ -6,6 +6,8 @@ class DeployTracker {
constructor(embark, options) {
this.logger = embark.logger;
this.events = embark.events;
this.embark = embark;
this.trackContracts = (options.trackContracts !== false);
// TODO: unclear where it comes from
this.env = options.env;
@ -17,28 +19,36 @@ class DeployTracker {
registerEvents() {
const self = this;
this.events.on("deploy:beforeAll", this.setCurrentChain.bind(this));
this.embark.registerActionForEvent("deploy:beforeAll", this.setCurrentChain.bind(this));
this.events.on("deploy:contract:deployed", (contract) => {
self.trackContract(contract.className, contract.realRuntimeBytecode, contract.realArgs, contract.deployedAddress);
self.save();
});
this.events.setCommandHandler("deploy:contract:shouldDeploy", (contract, cb) => {
self.embark.registerActionForEvent("deploy:contract:shouldDeploy", (params, cb) => {
if (!self.trackContracts) {
return cb(params);
}
let contract = params.contract;
let trackedContract = self.getContract(contract.className, contract.realRuntimeBytecode, contract.realArgs);
cb(trackedContract);
if (trackedContract) {
params.contract.address = trackedContract.address;
}
if (params.shouldDeploy && trackedContract) {
params.shouldDeploy = true;
}
cb(params);
});
}
// TODO: just an event might not be enought to the async nature
// it needs to be a plugin api before deploy, that makes the deployment wait
setCurrentChain() {
setCurrentChain(cb) {
const self = this;
if (this.chainConfig === false) {
this.currentChain = {contracts: []};
//return cb();
return cb();
}
this.events.request("blockchain:block:byNumber", 0, function(_err, block) {
let chainId = block.hash;
@ -49,7 +59,7 @@ class DeployTracker {
self.currentChain = self.chainConfig[chainId];
self.currentChain.name = self.env;
//cb();
cb();
});
}

View File

@ -0,0 +1,26 @@
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

@ -0,0 +1,99 @@
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

@ -0,0 +1,38 @@
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

@ -0,0 +1,193 @@
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) {
// FIXME Calling ens.owner makes the transaction fail on privatenet for some reason
// address currentOwner = ens.owner(node);
// require(currentOwner == 0 || currentOwner == msg.sender);
require(true == true);
_;
}
/**
* 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,98 +1,8 @@
import namehash from 'eth-ens-namehash';
/*global EmbarkJS, web3, registerSubDomain, namehash*/
/*global web3, EmbarkJS*/
let __embarkENS = {};
// registry interface for later
__embarkENS.registryInterface = [
{
"constant": true,
"inputs": [
{
"name": "node",
"type": "bytes32"
}
],
"name": "resolver",
"outputs": [
{
"name": "",
"type": "address"
}
],
"type": "function"
},
{
"constant": true,
"inputs": [
{
"name": "node",
"type": "bytes32"
}
],
"name": "owner",
"outputs": [
{
"name": "",
"type": "address"
}
],
"type": "function"
},
{
"constant": false,
"inputs": [
{
"name": "node",
"type": "bytes32"
},
{
"name": "resolver",
"type": "address"
}
],
"name": "setResolver",
"outputs": [],
"type": "function"
},
{
"constant": false,
"inputs": [
{
"name": "node",
"type": "bytes32"
},
{
"name": "label",
"type": "bytes32"
},
{
"name": "owner",
"type": "address"
}
],
"name": "setSubnodeOwner",
"outputs": [],
"type": "function"
},
{
"constant": false,
"inputs": [
{
"name": "node",
"type": "bytes32"
},
{
"name": "owner",
"type": "address"
}
],
"name": "setOwner",
"outputs": [],
"type": "function"
}
];
// resolver interface
__embarkENS.resolverInterface = [
{
"constant": true,
@ -145,27 +55,6 @@ __embarkENS.resolverInterface = [
],
"type": "function"
},
{
"constant": true,
"inputs": [
{
"name": "node",
"type": "bytes32"
},
{
"name": "kind",
"type": "bytes32"
}
],
"name": "has",
"outputs": [
{
"name": "",
"type": "bool"
}
],
"type": "function"
},
{
"constant": false,
"inputs": [
@ -242,6 +131,13 @@ __embarkENS.resolverInterface = [
}
];
const defaultAccountNotSetError = 'web3.eth.defaultAccount not set';
const providerNotSetError = 'ENS provider not set';
const NoDecodeAddrError = 'Error: Couldn\'t decode address from ABI: 0x';
const NoDecodeStringError = 'ERROR: The returned value is not a convertible string: 0x0';
const reverseAddrSuffix = '.addr.reverse';
const voidAddress = '0x0000000000000000000000000000000000000000';
__embarkENS.registryAddresses = {
// Mainnet
"1": "0x314159265dd8dbb310642f98f50c066173c1259b",
@ -251,56 +147,114 @@ __embarkENS.registryAddresses = {
"4": "0xe7410170f87102DF0055eB195163A03B7F2Bff4A"
};
__embarkENS.setProvider = function () {
__embarkENS.setProvider = function (config) {
const self = this;
// get network id and then assign ENS contract based on that
let registryAddresses = this.registryAddresses;
this.ens = null;
web3.eth.net.getId().then(id => {
if (registryAddresses[id] !== undefined) {
EmbarkJS.onReady(() => {
self.ens = new EmbarkJS.Contract({abi: self.registryInterface, address: registryAddresses[id]});
const ERROR_MESSAGE = 'ENS is not available in this chain';
self.registration = config.registration;
self.env = config.env;
EmbarkJS.onReady(() => {
web3.eth.net.getId()
.then((id) => {
const registryAddress = self.registryAddresses[id] || config.registryAddress;
self._isAvailable = true;
self.ens = new EmbarkJS.Blockchain.Contract({abi: config.registryAbi, address: registryAddress, web3: web3});
self.registrar = new EmbarkJS.Blockchain.Contract({abi: config.registrarAbi, address: config.registrarAddress, web3: web3});
self.resolver = new EmbarkJS.Blockchain.Contract({abi: config.resolverAbi, address: config.resolverAddress, web3: web3});
})
.catch(err => {
if (err.message.indexOf('Provider not set or invalid') > -1) {
console.warn(ERROR_MESSAGE);
return;
}
console.error(err);
});
}
// todo: deploy at this point
}).catch(e => {
if (e.message.indexOf('Provider not set or invalid') > -1) {
console.warn('ENS is not available in this chain');
return;
}
console.error(e);
});
};
__embarkENS.resolve = function(name) {
const self = this;
if (self.ens === undefined) return;
__embarkENS.resolve = function (name, callback) {
callback = callback || function () {};
if (!this.ens) {
return callback(providerNotSetError);
}
if (!web3.eth.defaultAccount) {
return callback(defaultAccountNotSetError);
}
let node = namehash.hash(name);
return self.ens.methods.resolver(node).call().then((resolverAddress) => {
let resolverContract = new EmbarkJS.Contract({abi: self.resolverInterface, address: resolverAddress});
return resolverContract.methods.addr(node).call();
}).then((addr) => {
return addr;
}).catch(err => err);
function cb(err, addr) {
if (err === NoDecodeAddrError) {
return callback(name + " is not registered", "0x");
}
callback(err, addr);
}
return this.ens.methods.resolver(node).call((err, resolverAddress) => {
if (err) {
return cb(err);
}
if (resolverAddress === voidAddress) {
return cb('Name not yet registered');
}
let resolverContract = new EmbarkJS.Blockchain.Contract({abi: this.resolverInterface, address: resolverAddress, web3: web3});
resolverContract.methods.addr(node).call(cb);
});
};
__embarkENS.lookup = function(address) {
const self = this;
__embarkENS.lookup = function (address, callback) {
callback = callback || function () {};
if (!this.ens) {
return callback(providerNotSetError);
}
if (!web3.eth.defaultAccount) {
return callback(defaultAccountNotSetError);
}
if (address.startsWith("0x")) {
address = address.slice(2);
}
let node = web3.utils.soliditySha3(address.toLowerCase() + reverseAddrSuffix);
if (self.ens === undefined) return;
function cb(err, name) {
if (err === NoDecodeStringError || err === NoDecodeAddrError) {
return callback('Address does not resolve to name. Try syncing chain.');
}
return callback(err, name);
}
if (address.startsWith("0x")) address = address.slice(2);
let node = namehash.hash(address.toLowerCase() + ".addr.reverse");
return self.ens.methods.resolver(node).call().then((resolverAddress) => {
let resolverContract = new EmbarkJS.Contract({abi: self.resolverInterface, address: resolverAddress});
return resolverContract.methods.name(node).call();
}).then((name) => {
if (name === "" || name === undefined) throw Error("ENS name not found");
return name;
}).catch(err => err);
return this.ens.methods.resolver(node).call((err, resolverAddress) => {
if (err) {
return cb(err);
}
if (resolverAddress === voidAddress) {
return cb('Address not associated to a resolver');
}
let resolverContract = new EmbarkJS.Blockchain.Contract({abi: this.resolverInterface, address: resolverAddress, web3: web3});
resolverContract.methods.name(node).call(cb);
});
};
__embarkENS.registerSubDomain = function (name, address, callback) {
callback = callback || function () {};
if (!web3.eth.defaultAccount) {
return callback(defaultAccountNotSetError);
}
if (this.env !== 'development' && this.env !== 'privatenet') {
return callback('Sub-domain registration is only available in development or privatenet mode');
}
if (!this.registration || !this.registration.rootDomain) {
return callback('No rootDomain is declared in config/namesystem.js (register.rootDomain). Unable to register a subdomain until then.');
}
if (!address || !web3.utils.isAddress(address)) {
return callback('You need to specify a valid address for the subdomain');
}
// Register function generated by the index
registerSubDomain(this.ens, this.registrar, this.resolver, web3.eth.defaultAccount, name, this.registration.rootDomain,
web3.utils.soliditySha3(address.toLowerCase().substr(2) + reverseAddrSuffix), address, console, EmbarkJS.Utils.secureSend, callback);
};
__embarkENS.isAvailable = function () {
return Boolean(this._isAvailable);
};

View File

@ -1,28 +1,204 @@
const fs = require('../../core/fs.js');
const utils = require('../../utils/utils.js');
const namehash = require('eth-ens-namehash');
const async = require('async');
const embarkJsUtils = require('embarkjs').Utils;
const reverseAddrSuffix = '.addr.reverse';
class ENS {
constructor(embark, _options) {
this.env = embark.env;
this.isDev = embark.config.blockchainConfig.isDev;
this.logger = embark.logger;
this.events = embark.events;
this.namesConfig = embark.config.namesystemConfig;
this.registration = this.namesConfig.register || {};
this.embark = embark;
if (this.namesConfig === {} ||
this.namesConfig.enabled !== true ||
this.namesConfig.available_providers.indexOf('ens') < 0) {
return;
}
this.doSetENSProvider = this.namesConfig.provider === 'ens';
this.addENSToEmbarkJS();
this.addSetProvider();
this.configureContracts();
this.registerEvents();
}
registerEvents() {
this.events.once("contracts:deploy:afterAll", this.setProviderAndRegisterDomains.bind(this));
this.events.setCommandHandler("storage:ens:associate", this.associateStorageToEns.bind(this));
}
setProviderAndRegisterDomains(cb = (() => {})) {
const self = this;
async.parallel([
function getENSRegistry(paraCb) {
self.events.request('contracts:contract', "ENSRegistry", (contract) => {
paraCb(null, contract);
});
},
function getRegistrar(paraCb) {
self.events.request('contracts:contract', "FIFSRegistrar", (contract) => {
paraCb(null, contract);
});
},
function getResolver(paraCb) {
self.events.request('contracts:contract', "Resolver", (contract) => {
paraCb(null, contract);
});
}
], (err, results) => {
if (err) {
return cb(err);
}
if (!results[0] || !results[1] || !results[2]) {
return cb(__('Aborting ENS setup as some of the contracts did not deploy properly'));
}
// result[0] => ENSRegistry; result[1] => FIFSRegistrar; result[2] => FIFSRegistrar
let config = {
env: self.env,
registration: self.registration,
registryAbi: results[0].abiDefinition,
registryAddress: results[0].deployedAddress,
registrarAbi: results[1].abiDefinition,
registrarAddress: results[1].deployedAddress,
resolverAbi: results[2].abiDefinition,
resolverAddress: results[2].deployedAddress
};
if (self.doSetENSProvider) {
self.addSetProvider(config);
}
if ((!self.isDev && self.env !== 'privatenet') || !self.registration || !self.registration.subdomains || !Object.keys(self.registration.subdomains).length) {
return cb();
}
self.registerConfigDomains(config, cb);
});
}
associateStorageToEns(options, cb) {
const self = this;
// Code inspired by https://github.com/monkybrain/ipfs-to-ens
const {name, storageHash} = options;
if (!utils.isValidEthDomain(name)) {
return cb('Invalid domain name ' + name);
}
let hashedName = namehash.hash(name);
let contentHash;
try {
contentHash = utils.hashTo32ByteHexString(storageHash);
} catch (e) {
return cb('Invalid IPFS hash');
}
// Set content
async.waterfall([
function getRegistryABI(next) {
self.events.request('contracts:contract', "ENSRegistry", (contract) => {
next(null, contract);
});
},
function createRegistryContract(contract, next) {
self.events.request("blockchain:contract:create",
{abi: contract.abiDefinition, address: contract.deployedAddress},
(resolver) => {
next(null, resolver);
});
},
function getResolverForName(registry, next) {
registry.methods.resolver(hashedName).call((err, resolverAddress) => {
if (err) {
return cb(err);
}
if (resolverAddress === '0x0000000000000000000000000000000000000000') {
return cb('Name not yet registered');
}
next(null, resolverAddress);
});
},
function getResolverABI(resolverAddress, next) {
self.events.request('contracts:contract', "Resolver", (contract) => {
next(null, resolverAddress, contract);
});
},
function createResolverContract(resolverAddress, contract, next) {
self.events.request("blockchain:contract:create",
{abi: contract.abiDefinition, address: resolverAddress},
(resolver) => {
next(null, resolver);
});
},
function getDefaultAccount(resolver, next) {
self.events.request("blockchain:defaultAccount:get", (defaultAccount) => {
next(null, resolver, defaultAccount);
});
},
function setContent(resolver, defaultAccount, next) {
resolver.methods.setContent(hashedName, contentHash).send({from: defaultAccount}).then((transaction) => {
if (transaction.status !== "0x1" && transaction.status !== "0x01" && transaction.status !== true) {
return next('Association failed. Status: ' + transaction.status);
}
next();
}).catch(next);
}
], cb);
}
registerConfigDomains(config, cb) {
const self = this;
const register = require('./register');
const secureSend = embarkJsUtils.secureSend;
self.events.request("blockchain:defaultAccount:get", (defaultAccount) => {
async.parallel([
function createRegistryContract(paraCb) {
self.events.request("blockchain:contract:create",
{abi: config.registryAbi, address: config.registryAddress},
(registry) => {
paraCb(null, registry);
});
},
function createRegistrarContract(paraCb) {
self.events.request("blockchain:contract:create",
{abi: config.registrarAbi, address: config.registrarAddress},
(registrar) => {
paraCb(null, registrar);
});
},
function createResolverContract(paraCb) {
self.events.request("blockchain:contract:create",
{abi: config.resolverAbi, address: config.resolverAddress},
(resolver) => {
paraCb(null, resolver);
});
}
], function(err, contracts) {
if (err) {
return cb(err);
}
const [ens, registrar, resolver] = contracts;
async.each(Object.keys(self.registration.subdomains), (subDomainName, eachCb) => {
const address = self.registration.subdomains[subDomainName];
const reverseNode = utils.soliditySha3(address.toLowerCase().substr(2) + reverseAddrSuffix);
register(ens, registrar, resolver, defaultAccount, subDomainName, self.registration.rootDomain,
reverseNode, address, self.logger, secureSend, eachCb);
}, cb);
});
});
}
addENSToEmbarkJS() {
const self = this;
// TODO: make this a shouldAdd condition
if (this.namesConfig === {}) {
return;
}
if ((this.namesConfig.available_providers.indexOf('ens') < 0) && (this.namesConfig.provider !== 'ens' || this.namesConfig.enabled !== true)) {
return;
}
// get namehash, import it into file
self.events.request("version:get:eth-ens-namehash", function(EnsNamehashVersion) {
let currentEnsNamehashVersion = require('../../../package.json').dependencies["eth-ens-namehash"];
if (EnsNamehashVersion !== currentEnsNamehashVersion) {
@ -32,23 +208,119 @@ class ENS {
}
});
let code = "";
let code = fs.readFileSync(utils.joinPath(__dirname, 'register.js')).toString();
code += "\n" + fs.readFileSync(utils.joinPath(__dirname, 'embarkjs.js')).toString();
code += "\nEmbarkJS.Names.registerProvider('ens', __embarkENS);";
this.embark.addCodeToEmbarkJS(code);
}
addSetProvider() {
let config = JSON.stringify({});
configureContracts() {
const config = {
"default": {
"gas": "auto",
"contracts": {
"ENS": {
"deploy": false,
"silent": true
},
"ENSRegistry": {
"deploy": true,
"silent": true,
"args": []
},
"Resolver": {
"deploy": true,
"silent": true,
"args": ["$ENSRegistry"]
},
"FIFSRegistrar": {
"deploy": false
}
}
},
"ropsten": {
"contracts": {
"ENSRegistry": {
"address": "0x112234455c3a32fd11230c42e7bccd4a84e02010",
"silent": true
},
"Resolver": {
"deploy": false
},
"FIFSRegistrar": {
"deploy": false
}
}
},
"rinkeby": {
"contracts": {
"ENSRegistry": {
"address": "0xe7410170f87102DF0055eB195163A03B7F2Bff4A",
"silent": true
},
"Resolver": {
"deploy": false
},
"FIFSRegistrar": {
"deploy": false
}
}
},
"livenet": {
"contracts": {
"ENSRegistry": {
"address": "0x314159265dd8dbb310642f98f50c066173c1259b",
"silent": true
},
"Resolver": {
"deploy": false
},
"FIFSRegistrar": {
"deploy": false
}
}
}
};
config.testnet = config.ropsten;
let code = "\nEmbarkJS.Names.setProvider('ens'," + config + ");";
if (this.registration && this.registration.rootDomain) {
// Register root domain if it is defined
const rootNode = namehash.hash(this.registration.rootDomain);
config.default.contracts['FIFSRegistrar'] = {
"deploy": true,
"silent": true,
"args": ["$ENSRegistry", rootNode],
"onDeploy": [
`ENSRegistry.methods.setOwner('${rootNode}', web3.eth.defaultAccount).send({from: web3.eth.defaultAccount}).then(() => {
ENSRegistry.methods.setResolver('${rootNode}', "$Resolver").send({from: web3.eth.defaultAccount});
var reverseNode = web3.utils.soliditySha3(web3.eth.defaultAccount.toLowerCase().substr(2) + '${reverseAddrSuffix}');
ENSRegistry.methods.setResolver(reverseNode, "$Resolver").send({from: web3.eth.defaultAccount});
Resolver.methods.setAddr('${rootNode}', web3.eth.defaultAccount).send({from: web3.eth.defaultAccount});
Resolver.methods.setName(reverseNode, '${this.registration.rootDomain}').send({from: web3.eth.defaultAccount});
})`
]
};
}
config.privatenet = config.development;
this.embark.registerContractConfiguration(config);
//if (this.isDev || this.env === 'privatenet') {
this.embark.events.request("config:contractsFiles:add", this.embark.pathToFile('./contracts/ENSRegistry.sol'));
this.embark.events.request("config:contractsFiles:add", this.embark.pathToFile('./contracts/FIFSRegistrar.sol'));
this.embark.events.request("config:contractsFiles:add", this.embark.pathToFile('./contracts/Resolver.sol'));
//}
}
addSetProvider(config) {
let code = "\nEmbarkJS.Names.setProvider('ens'," + JSON.stringify(config) + ");";
let shouldInit = (namesConfig) => {
return (namesConfig.provider === 'ens' && namesConfig.enabled === true);
};
this.embark.addProviderInit('names', code, shouldInit);
this.embark.addConsoleProviderInit('names', code, shouldInit);
}
}

View File

@ -0,0 +1,46 @@
/*global web3*/
const namehash = require('eth-ens-namehash');
function registerSubDomain(ens, registrar, resolver, defaultAccount, subdomain, rootDomain, reverseNode, address, logger, secureSend, callback) {
const subnode = namehash.hash(subdomain);
const rootNode = namehash.hash(rootDomain);
const node = namehash.hash(`${subdomain}.${rootDomain}`);
// FIXME Registrar calls a function in ENS and in privatenet it doesn't work for soem reason
// const toSend = registrar.methods.register(subnode, defaultAccount);
const toSend = ens.methods.setSubnodeOwner(rootNode, subnode, defaultAccount);
let transaction;
secureSend(web3, toSend, {from: defaultAccount}, false)
// Set resolver for the node
.then(transac => {
if (transac.status !== "0x1" && transac.status !== "0x01" && transac.status !== true) {
logger.warn('Failed transaction', transac);
return callback('Failed to register. Check gas cost.');
}
transaction = transac;
return secureSend(web3, ens.methods.setResolver(node, resolver.options.address), {from: defaultAccount}, false);
})
// Set address for node
.then(_result => {
return secureSend(web3, resolver.methods.setAddr(node, address), {from: defaultAccount}, false);
})
// Set resolver for the reverse node
.then(_result => {
return secureSend(web3, ens.methods.setResolver(reverseNode, resolver.options.address), {from: defaultAccount}, false);
})
// Set name for reverse node
.then(_result => {
return secureSend(web3, resolver.methods.setName(reverseNode, `${subdomain}.${rootDomain}`), {from: defaultAccount}, false);
})
.then(_result => {
callback(null, transaction);
})
.catch(err => {
logger.error(err.message || err);
callback('Failed to register with error: ' + (err.message || err));
});
}
if (typeof module !== 'undefined' && module.exports) {
module.exports = registerSubDomain;
}

152
lib/modules/graph/index.js Normal file
View File

@ -0,0 +1,152 @@
const async = require('async');
const Viz = require('viz.js');
const fs = require('fs');
class GraphGenerator {
constructor(embark, _options) {
const self = this;
this.events = embark.events;
this.contracts = [];
this.events.setCommandHandler("graph:create", function(options, cb) {
self.generate(options);
cb();
});
}
generate(options) {
const self = this;
let id = 0;
let contractString = "";
let relationshipString = "";
let idMapping = {};
let contractInheritance = {};
let contractsDependencies = {};
async.waterfall([
function getContractList(next) {
self.events.request('contracts:list', (err, contracts) => {
self.contracts = contracts;
next();
});
},
function getContractsDependencies(next) {
self.events.request('contracts:dependencies', (err, _contractsDependencies) => {
contractsDependencies = _contractsDependencies;
next();
});
},
function (next) {
for (let contract of self.contracts) {
if (options.skipUndeployed && !contract.deploy) continue;
id++;
idMapping[contract.className] = id;
let contractLabel = "";
contractLabel += `${contract.className}`;
let tooltip = contract.className;
if (contract.instanceOf !== undefined && self.getContract(contract.instanceOf) !== undefined) {
contractInheritance[contract.className] = contract.instanceOf;
contractLabel += ": " + contract.instanceOf;
tooltip += " instance of " + contract.instanceOf;
} else {
if (!(options.skipFunctions === true && options.skipEvents === true)) contractLabel += "|";
for (let i = 0; i < contract.abiDefinition.length; i++) {
let abiDef = contract.abiDefinition[i];
if (abiDef.type == 'event' && options.skipEvents) continue;
if (['constructor', 'fallback'].indexOf(abiDef.type) > -1 && options.skipFunctions) continue;
switch(abiDef.type){
case 'fallback':
contractLabel += "«fallback»()\\l";
break;
case 'constructor':
contractLabel += "«constructor»(";
abiDef.inputs.forEach(function(elem, index){
contractLabel += (index == 0 ? "" : ", ") + elem.type;
});
contractLabel += ")\\l";
break;
case 'event':
contractLabel += "«event»" + abiDef.name + "(";
abiDef.inputs.forEach(function(elem, index){
contractLabel += (index == 0 ? "" : ", ") + elem.type;
});
contractLabel += ")\\l";
break;
default: break;
}
}
let fHashes = contract.functionHashes;
if (fHashes != {} && fHashes != undefined && !options.skipFunctions){
for (let method in contract.functionHashes){
contractLabel += method + '\\l';
}
}
}
let others = '';
if (!contract.deploy){
others = 'fontcolor="#c3c3c3", color="#a0a0a0"';
tooltip += " (not deployed)";
}
contractString += `${id}[label = "{${contractLabel}}", tooltip="${tooltip}", fillcolor=gray95, ${others}]\n`;
}
next();
},
function (next) {
for (let c in contractsDependencies) {
let contractDependencies = Array.from(new Set(contractsDependencies[c]));
contractDependencies.forEach((d) => {
if (idMapping[c] !== undefined && idMapping[d] !== undefined) {
if ((options.skipUndeployed && self.getContract(c).deploy && self.getContract(d).deploy) || !options.skipUndeployed) {
relationshipString += `${idMapping[d]}->${idMapping[c]}[constraint=true, arrowtail=diamond, tooltip="${c} uses ${d}"]\n`;
}
}
});
}
next();
},
function (next) {
for (let c in contractInheritance){
if(options.skipUndeployed && !self.getContract(contractInheritance[c]).deploy) continue;
relationshipString += `${idMapping[contractInheritance[c]]}->${idMapping[c]}[tooltip="${c} instance of ${contractInheritance[c]}"]\n`;
}
next();
},
function (next) {
let dot = `
digraph Contracts {
node[shape=record,style=filled]
edge[dir=back, arrowtail=empty]
${contractString}
${relationshipString}
}`;
let svg = Viz(dot);
let filename = "diagram.svg";
fs.writeFileSync(filename, svg, (err) => {
if (err) throw err;
next();
});
}
], function(_err, _result) {
});
}
getContract(contractName) {
return this.contracts.find((contract) => { return contract.className === contractName; });
}
}
module.exports = GraphGenerator;

View File

@ -1,17 +1,19 @@
import IpfsApi from 'ipfs-api';
/*global IpfsApi*/
let __embarkIPFS = {};
const __embarkIPFS = {};
const NoConnectionError = 'No IPFS connection. Please ensure to call Embark.Storage.setProvider()';
__embarkIPFS.setProvider = function (options) {
var self = this;
var promise = new Promise(function (resolve, reject) {
const self = this;
return new Promise(function (resolve, reject) {
try {
if (options === undefined) {
if (!options) {
self._config = options;
self._ipfsConnection = IpfsApi('localhost', '5001');
self._getUrl = "http://localhost:8080/ipfs/";
} else {
var ipfsOptions = {host: options.host || options.server, protocol: 'http'};
const ipfsOptions = {host: options.host || options.server, protocol: 'http'};
if (options.protocol) {
ipfsOptions.protocol = options.protocol;
}
@ -28,77 +30,6 @@ __embarkIPFS.setProvider = function (options) {
reject(new Error('Failed to connect to IPFS'));
}
});
return promise;
};
__embarkIPFS.saveText = function (text) {
const self = this;
var promise = new Promise(function (resolve, reject) {
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) {
if (err) {
reject(err);
} else {
resolve(result[0].path);
}
});
});
return promise;
};
__embarkIPFS.get = function (hash) {
const self = this;
// TODO: detect type, then convert if needed
//var ipfsHash = web3.toAscii(hash);
var promise = new Promise(function (resolve, reject) {
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) {
if (err) {
return reject(err);
}
resolve(files[0].content.toString());
});
});
return promise;
};
__embarkIPFS.uploadFile = function (inputSelector) {
const self = this;
var file = inputSelector[0].files[0];
if (file === undefined) {
throw new Error('no file found');
}
var promise = new Promise(function (resolve, reject) {
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) {
if (err) {
reject(err);
} else {
resolve(result[0].path);
}
});
};
reader.readAsArrayBuffer(file);
});
return promise;
};
__embarkIPFS.isAvailable = function () {
@ -110,14 +41,108 @@ __embarkIPFS.isAvailable = function () {
.then((id) => {
resolve(Boolean(id));
})
.catch(() => {
.catch((err) => {
console.error(err);
resolve(false);
});
});
};
__embarkIPFS.saveText = function (text) {
const self = this;
return new Promise(function (resolve, reject) {
if (!self._ipfsConnection) {
return reject(new Error(NoConnectionError));
}
self._ipfsConnection.add(self._ipfsConnection.Buffer.from(text), function (err, result) {
if (err) {
return reject(err);
}
resolve(result[0].path);
});
});
};
__embarkIPFS.get = function (hash) {
const self = this;
// TODO: detect type, then convert if needed
//var ipfsHash = web3.toAscii(hash);
return new Promise(function (resolve, reject) {
if (!self._ipfsConnection) {
var connectionError = new Error(NoConnectionError);
return reject(connectionError);
}
self._ipfsConnection.get(hash, function (err, files) {
if (err) {
return reject(err);
}
resolve(files[0].content.toString());
});
});
};
__embarkIPFS.uploadFile = function (inputSelector) {
const self = this;
const file = inputSelector[0].files[0];
if (file === undefined) {
throw new Error('no file found');
}
return new Promise(function (resolve, reject) {
if (!self._ipfsConnection) {
return reject(new Error(NoConnectionError));
}
const reader = new FileReader();
reader.onloadend = function () {
const buffer = self._ipfsConnection.Buffer.from(reader.result);
self._ipfsConnection.add(buffer, function (err, result) {
if (err) {
return reject(err);
}
resolve(result[0].path);
});
};
reader.readAsArrayBuffer(file);
});
};
__embarkIPFS.getUrl = function (hash) {
return (this._getUrl || "http://localhost:8080/ipfs/") + hash;
};
__embarkIPFS.resolve = function (name, callback) {
callback = callback || function () {};
if (!this._ipfsConnection) {
return callback(new Error(NoConnectionError));
}
this._ipfsConnection.name.resolve(name)
.then(res => {
callback(null, res.Path);
})
.catch(() => {
callback(name + " is not registered");
});
};
__embarkIPFS.register = function(addr, callback) {
callback = callback || function () {};
if (!this._ipfsConnection) {
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)
.then(res => {
callback(null, res.Name);
})
.catch(() => {
callback(addr + " could not be registered");
});
};

Some files were not shown because too many files have changed in this diff Show More