mirror of
https://github.com/embarklabs/embark.git
synced 2025-02-26 12:25:49 +00:00
commit
142f04df06
@ -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
24
.github/ISSUE_TEMPLATE.md
vendored
Normal 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
11
.github/PULL_REQUEST_TEMPLATE.md
vendored
Normal 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
1
.gitignore
vendored
@ -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
|
||||
|
11
.travis.yml
11
.travis.yml
@ -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
|
||||
|
@ -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']
|
@ -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/).
|
||||
|
14
appveyor.yml
14
appveyor.yml
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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
551
cmd/cmd_controller.js
Normal 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;
|
45
cmd/dashboard/dashboard.js
Normal file
45
cmd/dashboard/dashboard.js
Normal 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;
|
@ -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
43
cmd/dashboard/repl.js
Normal 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;
|
397
js/embark.js
397
js/embark.js
@ -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;
|
@ -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;
|
149
js/mine.js
149
js/mine.js
@ -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
1
js/web3-1.0.min.js
vendored
File diff suppressed because one or more lines are too long
@ -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;
|
@ -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);
|
||||
};
|
@ -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;
|
@ -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;
|
@ -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;
|
@ -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
|
||||
};
|
@ -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",
|
||||
|
@ -1 +0,0 @@
|
||||
__mainContext.<%- className %> = new EmbarkJS.Contract({abi: <%- abi %>, address: <%- contractAddress %>, code: '<%- contract.code %>', gasEstimates: <%- gasEstimates %>});
|
@ -1 +0,0 @@
|
||||
var __mainContext = __mainContext || this;
|
@ -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);
|
||||
};
|
||||
|
@ -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 %>
|
||||
});
|
||||
});
|
@ -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;
|
@ -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;
|
@ -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) => {
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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);
|
||||
});
|
||||
}
|
||||
|
@ -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
36
lib/core/i18n/i18n.js
Normal 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
|
||||
};
|
@ -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() {
|
||||
|
@ -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) {
|
||||
|
101
lib/core/modules/coderunner/codeRunner.js
Normal file
101
lib/core/modules/coderunner/codeRunner.js
Normal 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;
|
44
lib/core/modules/coderunner/runCode.js
Normal file
44
lib/core/modules/coderunner/runCode.js
Normal 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;
|
@ -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;
|
||||
|
@ -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);
|
||||
};
|
||||
|
@ -1,5 +1,5 @@
|
||||
const uuid = require('uuid/v1');
|
||||
const constants = require('../constants');
|
||||
const constants = require('../../constants');
|
||||
|
||||
class Events {
|
||||
|
@ -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);
|
||||
}
|
34
lib/core/processes/processManager.js
Normal file
34
lib/core/processes/processManager.js
Normal 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;
|
@ -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;
|
@ -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);
|
||||
};
|
@ -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;
|
@ -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;
|
@ -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
|
||||
};
|
||||
|
@ -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):"
|
||||
}
|
@ -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}}...."
|
||||
}
|
@ -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}}...."
|
||||
}
|
361
lib/index.js
361
lib/index.js
@ -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;
|
||||
|
@ -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;
|
||||
|
84
lib/modules/blockchain_connector/provider.js
Normal file
84
lib/modules/blockchain_connector/provider.js
Normal 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;
|
@ -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;
|
@ -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();
|
||||
}
|
@ -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');
|
||||
});
|
@ -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');
|
89
lib/modules/blockchain_process/index.js
Normal file
89
lib/modules/blockchain_process/index.js
Normal 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;
|
325
lib/modules/blockchain_process/miner.js
Normal file
325
lib/modules/blockchain_process/miner.js
Normal 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;
|
154
lib/modules/blockchain_process/proxy.js
Normal file
154
lib/modules/blockchain_process/proxy.js
Normal 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);
|
||||
});
|
||||
});
|
||||
};
|
84
lib/modules/blockchain_process/simulator.js
Normal file
84
lib/modules/blockchain_process/simulator.js
Normal 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;
|
@ -1,6 +1,7 @@
|
||||
whenEnvIsLoaded(function(){
|
||||
__mainContext.__loadManagerInstance.doFirst(function(done) {
|
||||
<%- block %>
|
||||
})
|
||||
});
|
||||
});
|
||||
|
||||
EmbarkJS.environment = "<%- environment %>";
|
||||
});
|
@ -0,0 +1 @@
|
||||
__mainContext.<%- className %> = new EmbarkJS.Blockchain.Contract({abi: <%- abi %>, address: <%- contractAddress %>, code: '<%- contract.code %>', gasEstimates: <%- gasEstimates %>});
|
@ -0,0 +1,3 @@
|
||||
var __mainContext = __mainContext || (
|
||||
this ? this : typeof self !== 'undefined' ? self : void 0
|
||||
);
|
@ -0,0 +1,3 @@
|
||||
EmbarkJS.Blockchain.connect(<%- connectionList %>, {warnAboutMetamask: <%= warnAboutMetamask %>}, function(err) {
|
||||
<%- done %>
|
||||
});
|
@ -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;
|
@ -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);
|
||||
});
|
117
lib/modules/console/index.js
Normal file
117
lib/modules/console/index.js
Normal 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;
|
@ -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;
|
||||
|
@ -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;
|
||||
|
347
lib/modules/coverage/contract_source.js
Normal file
347
lib/modules/coverage/contract_source.js
Normal 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;
|
97
lib/modules/coverage/contract_sources.js
Normal file
97
lib/modules/coverage/contract_sources.js
Normal 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;
|
71
lib/modules/coverage/index.js
Normal file
71
lib/modules/coverage/index.js
Normal 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;
|
63
lib/modules/coverage/source_map.js
Normal file
63
lib/modules/coverage/source_map.js
Normal 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;
|
@ -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);
|
||||
});
|
175
lib/modules/deployment/index.js
Normal file
175
lib/modules/deployment/index.js
Normal 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;
|
@ -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();
|
||||
});
|
||||
}
|
||||
|
||||
|
26
lib/modules/ens/contracts/ENS.sol
Normal file
26
lib/modules/ens/contracts/ENS.sol
Normal 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);
|
||||
|
||||
}
|
99
lib/modules/ens/contracts/ENSRegistry.sol
Normal file
99
lib/modules/ens/contracts/ENSRegistry.sol
Normal 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;
|
||||
}
|
||||
|
||||
}
|
38
lib/modules/ens/contracts/FIFSRegistrar.sol
Normal file
38
lib/modules/ens/contracts/FIFSRegistrar.sol
Normal 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);
|
||||
}
|
||||
}
|
193
lib/modules/ens/contracts/Resolver.sol
Normal file
193
lib/modules/ens/contracts/Resolver.sol
Normal 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;
|
||||
}
|
||||
}
|
@ -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);
|
||||
};
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
46
lib/modules/ens/register.js
Normal file
46
lib/modules/ens/register.js
Normal 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
152
lib/modules/graph/index.js
Normal 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;
|
@ -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
Loading…
x
Reference in New Issue
Block a user