Add support for Parity

Addons
 - New chain initialization and genesis management
 - Option to choose client to use
 - Option to "ping forever" for Geth
 - Creation and unlock of accounts at client's start
 - Utility to fund accounts with ethers
 - Miner settings inside the ethereum client
 - Workaround to CORS problem: origin is now http://embark
 - Several double callback's checks

Updates
 - Boilerplate, templates, configuration files and demo stuff
 - Messages and i18n strings
 - Tests

Fixes
 - Geth client now uses miner.gastarget instead of the deprecated targetGasLimit
 - Workaround for shh_version with Parity

Reworks of other PRs into the new code
 - Included delayed proxy
 - Send ready only when the proxy is started
 - Start HTTP and WS proxies individually
 - Async setupProxy
 - Fixed datadir for GethMiner
This commit is contained in:
Giuseppe Bertone 2018-10-06 18:05:37 +02:00 committed by Pascal Precht
parent 25bfea48b1
commit 81e798c89c
No known key found for this signature in database
GPG Key ID: 0EE28D8D6FD85D7D
29 changed files with 1315 additions and 388 deletions

View File

@ -3,7 +3,7 @@ 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;
let embark = new EmbarkController();
// set PWD to process.cwd() since Windows doesn't have a value for PWD
if (!process.env.PWD) {
@ -130,7 +130,7 @@ 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 (supported: %s)', 'geth'))
.option('-c, --client [client]', __('Use a specific ethereum client [%s] (default: %s)', 'geth, parity', '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)'))
@ -141,7 +141,7 @@ class Cmd {
_options.logFile = _options.logfile; // fix casing
_options.logLevel = _options.loglevel; // fix casing
_options.onlyCompile = _options.contracts;
_options.client = _options.client || 'geth';
_options.client = _options.client;
_options.webpackConfigName = _options.pipeline || 'production';
embark.build(_options);
});
@ -151,7 +151,7 @@ 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 (supported: %s)', 'geth'))
.option('-c, --client [client]', __('Use a specific ethereum client [%s] (default: %s)', 'geth, parity', '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'))
@ -167,9 +167,9 @@ class Cmd {
const nullify = (v) => (!v || typeof v !== 'string') ? null : v;
embark.run({
env: env || 'development',
serverPort: nullify(options.port),
serverHost: nullify(options.host),
client: options.client || 'geth',
serverPort: options.port,
serverHost: options.host,
client: options.client,
locale: options.locale,
runWebserver: options.noserver == null ? null : !options.noserver,
useDashboard: !options.nodashboard,
@ -184,7 +184,7 @@ class Cmd {
console() {
program
.command('console [environment]')
.option('-c, --client [client]', __('Use a specific ethereum client (supported: %s)', 'geth'))
.option('-c, --client [client]', __('Use a specific ethereum client [%s] (default: %s)', 'geth, parity', '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)'))
@ -194,7 +194,7 @@ class Cmd {
i18n.setOrDetectLocale(options.locale);
embark.console({
env: env || 'development',
client: options.client || 'geth',
client: options.client,
locale: options.locale,
logFile: options.logfile,
logLevel: options.loglevel,
@ -206,7 +206,7 @@ class Cmd {
blockchain() {
program
.command('blockchain [environment]')
.option('-c, --client [client]', __('Use a specific ethereum client (supported: %s)', 'geth'))
.option('-c, --client [client]', __('Use a specific ethereum client [%s] (default: %s)', 'geth, parity', 'geth'))
.option('--locale [locale]', __('language to use (default: en)'))
.description(__('run blockchain server (default: %s)', 'development'))
.action(function(env, options) {
@ -215,7 +215,7 @@ class Cmd {
embarkConfig: 'embark.json',
interceptLogs: false
});
embark.blockchain(env || 'development', options.client || 'geth');
embark.blockchain(env || 'development', options.client);
});
}
@ -223,7 +223,7 @@ class Cmd {
program
.command('simulator [environment]')
.description(__('run a fast ethereum rpc simulator'))
.option('--testrpc', __('use testrpc as the rpc simulator [%s]', 'default'))
.option('--testrpc', __('use ganache-cli (former "testrpc") as the rpc simulator [%s]', 'default'))
.option('-p, --port [port]', __('port to run the rpc simulator (default: %s)', '8545'))
.option('-h, --host [host]', __('host to run the rpc simulator (default: %s)', 'localhost'))
.option('-a, --accounts [numAccounts]', __('number of accounts (default: %s)', '10'))
@ -288,7 +288,7 @@ class Cmd {
.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 (supported: %s)', 'geth'))
.option('-c, --client [client]', __('Use a specific ethereum client [%s] (default: %s)', 'geth, parity', 'geth'))
.option('--pipeline [pipeline]', __('webpack config to use (default: production)'))
.description(__('Upload your dapp to a decentralized storage') + '.')
.action(function(env, _options) {
@ -301,7 +301,7 @@ class Cmd {
_options.ensDomain = _options.ens;
_options.logFile = _options.logfile; // fix casing
_options.logLevel = _options.loglevel; // fix casing
_options.client = _options.client || 'geth';
_options.client = _options.client;
_options.webpackConfigName = _options.pipeline || 'production';
embark.upload(_options);
});

View File

@ -120,7 +120,7 @@ Config.prototype._updateBlockchainCors = function(){
}
// add whisper cors
if(this.communicationConfig && this.communicationConfig.enabled && this.communicationConfig.provider === 'whisper'){
corsParts.push('embark');
corsParts.push('http://embark');
}
let cors = corsParts.join(',');

View File

@ -232,6 +232,7 @@ class Engine {
web3Service(options) {
this.registerModule('blockchain_process', {
client: this.client,
locale: this.locale,
isDev: this.isDev,
ipc: this.ipc

View File

@ -19,7 +19,13 @@ class Provider {
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"}});
// Note: don't pass to the provider things like {headers: {Origin: "embark"}}. Origin header is for browser to fill
// to protect user, it has no meaning if it is used server-side. See here for more details: https://github.com/ethereum/go-ethereum/issues/16608
// Moreover, Parity reject origins that are not urls so if you try to connect with Origin: "embark" it gives the followin error:
// << Blocked connection to WebSockets server from untrusted origin: Some("embark") >>
// The best choice is to use void origin, BUT Geth rejects void origin, so to keep both clients happy we can use http://embark
self.provider = new this.web3.providers.WebsocketProvider(self.web3Endpoint, {headers: {Origin: "http://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 {

View File

@ -1,65 +1,65 @@
const async = require('async');
const child_process = require('child_process');
const {spawn, exec} = require('child_process');
const fs = require('../../core/fs.js');
const constants = require('../../constants.json');
const utils = require('../../utils/utils.js');
const GethCommands = require('./geth_commands.js');
const GethClient = require('./gethClient.js');
const ParityClient = require('./parityClient.js');
const DevFunds = require('./dev_funds.js');
const proxy = require('./proxy');
const Ipc = require('../../core/ipc');
const {defaultHost, dockerHostSwap} = require('../../utils/host');
/*eslint complexity: ["error", 37]*/
var Blockchain = function(options) {
this.blockchainConfig = options.blockchainConfig;
this.env = options.env || 'development';
this.client = options.client;
this.isDev = options.isDev;
this.onReadyCallback = options.onReadyCallback || (() => {});
this.onExitCallback = options.onExitCallback;
/*eslint complexity: ["error", 36]*/
var Blockchain = function(userConfig, clientClass) {
this.userConfig = userConfig;
this.env = userConfig.env || 'development';
this.isDev = userConfig.isDev;
this.onReadyCallback = userConfig.onReadyCallback || (() => {});
this.onExitCallback = userConfig.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', 'pubsub'];
if (this.isDev) {
defaultWsApi.push('personal');
}
let defaultWsApi = clientClass.DEFAULTS.WS_API;
if (this.isDev) defaultWsApi = clientClass.DEFAULTS.DEV_WS_API;
this.config = {
geth_bin: this.blockchainConfig.geth_bin || 'geth',
networkType: this.blockchainConfig.networkType || 'custom',
genesisBlock: this.blockchainConfig.genesisBlock || false,
datadir: this.blockchainConfig.datadir || false,
mineWhenNeeded: this.blockchainConfig.mineWhenNeeded || false,
rpcHost: dockerHostSwap(this.blockchainConfig.rpcHost) || defaultHost,
rpcPort: this.blockchainConfig.rpcPort || 8545,
rpcCorsDomain: this.blockchainConfig.rpcCorsDomain || false,
networkId: this.blockchainConfig.networkId || 1337,
port: this.blockchainConfig.port || 30303,
nodiscover: this.blockchainConfig.nodiscover || false,
mine: this.blockchainConfig.mine || false,
account: this.blockchainConfig.account || {},
whisper: (this.blockchainConfig.whisper === undefined) || this.blockchainConfig.whisper,
maxpeers: ((this.blockchainConfig.maxpeers === 0) ? 0 : (this.blockchainConfig.maxpeers || 25)),
bootnodes: this.blockchainConfig.bootnodes || "",
rpcApi: (this.blockchainConfig.rpcApi || ['eth', 'web3', 'net', 'debug']),
wsRPC: (this.blockchainConfig.wsRPC === undefined) || this.blockchainConfig.wsRPC,
wsHost: dockerHostSwap(this.blockchainConfig.wsHost) || defaultHost,
wsPort: this.blockchainConfig.wsPort || 8546,
wsOrigins: this.blockchainConfig.wsOrigins || false,
wsApi: (this.blockchainConfig.wsApi || defaultWsApi),
vmdebug: this.blockchainConfig.vmdebug || false,
targetGasLimit: this.blockchainConfig.targetGasLimit || false,
syncMode: this.blockchainConfig.syncMode,
syncmode: this.blockchainConfig.syncmode,
verbosity: this.blockchainConfig.verbosity
silent: this.userConfig.silent,
ethereumClientName: this.userConfig.ethereumClientName,
ethereumClientBin: this.userConfig.ethereumClientBin || this.userConfig.ethereumClientName || 'geth',
networkType: this.userConfig.networkType || clientClass.DEFAULTS.NETWORK_TYPE,
networkId: this.userConfig.networkId || clientClass.DEFAULTS.NETWORK_ID,
genesisBlock: this.userConfig.genesisBlock || false,
datadir: this.userConfig.datadir || false,
mineWhenNeeded: this.userConfig.mineWhenNeeded || false,
rpcHost: dockerHostSwap(this.userConfig.rpcHost) || defaultHost,
rpcPort: this.userConfig.rpcPort || 8545,
rpcCorsDomain: this.userConfig.rpcCorsDomain || false,
rpcApi: this.userConfig.rpcApi || clientClass.DEFAULTS.RPC_API,
port: this.userConfig.port || 30303,
nodiscover: this.userConfig.nodiscover || false,
mine: this.userConfig.mine || false,
account: this.userConfig.account || {},
devPassword: this.userConfig.devPassword || "",
whisper: (this.userConfig.whisper != false),
maxpeers: ((this.userConfig.maxpeers === 0) ? 0 : (this.userConfig.maxpeers || 25)),
bootnodes: this.userConfig.bootnodes || "",
wsRPC: (this.userConfig.wsRPC != false),
wsHost: dockerHostSwap(this.userConfig.wsHost) || defaultHost,
wsPort: this.userConfig.wsPort || 8546,
wsOrigins: this.userConfig.wsOrigins || false,
wsApi: this.userConfig.wsApi || defaultWsApi,
vmdebug: this.userConfig.vmdebug || false,
targetGasLimit: this.userConfig.targetGasLimit || false,
syncMode: this.userConfig.syncMode || this.userConfig.syncmode,
verbosity: this.userConfig.verbosity,
proxy: this.userConfig.proxy || true
};
if (this.blockchainConfig === {} || this.blockchainConfig.default) {
if (this.userConfig === {} || this.userConfig.default || JSON.stringify(this.userConfig) === '{"enabled":true}') {
this.config.account = {};
if (this.env === 'development') {
this.isDev = true;
@ -87,35 +87,25 @@ var Blockchain = function(options) {
process.exit();
}
this.initProxy();
this.client = new options.client({config: this.config, env: this.env, isDev: this.isDev});
this.client = new clientClass({config: this.config, env: this.env, isDev: this.isDev});
};
Blockchain.prototype.initProxy = function() {
this.config.proxy = true;
if (this.blockchainConfig.proxy === false) {
this.config.proxy = false;
return;
if (this.config.proxy) {
this.config.rpcPort += constants.blockchain.servicePortOnProxy;
this.config.wsPort += constants.blockchain.servicePortOnProxy;
}
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'});
if(!this.proxyIpc) this.proxyIpc = new Ipc({ipcRole: 'client'});
let wsProxy;
if(this.config.wsRPC) {
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
]);
[this.rpcProxy, this.wsProxy] = await Promise.all([proxy.serve(this.proxyIpc, this.config.rpcHost, this.config.rpcPort, false), wsProxy]);
};
Blockchain.prototype.shutdownProxy = function() {
@ -123,52 +113,55 @@ Blockchain.prototype.shutdownProxy = function() {
return;
}
if(this.rpcProxy) this.rpcProxy.close();
if(this.wsProxy) this.wsProxy.close();
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) {
if (this.config.silent) {
options.silent = true;
}
return child_process.exec(cmd, options, callback);
return exec(cmd, options, callback);
};
Blockchain.prototype.run = function() {
var self = this;
console.log("===============================================================================".magenta);
console.log("===============================================================================".magenta);
console.log(__("Embark Blockchain Using: %s", this.client.name.underline).magenta);
console.log(__("Embark Blockchain using %s", self.client.prettyName.underline).magenta);
console.log("===============================================================================".magenta);
console.log("===============================================================================".magenta);
this.checkPathLength();
if (self.client.name === 'geth') this.checkPathLength();
let address = '';
async.waterfall([
function checkInstallation(next) {
self.isClientInstalled((err) => {
if (err) {
console.log(__("could not find {{geth_bin}} command; is {{client_name}} installed or in the PATH?", {geth_bin: this.config.geth_bin, client_name: this.client.name}).green);
return next(err);
return next({message: err});
}
next();
});
},
function init(next) {
if (!self.isDev) {
return self.initChainAndGetAddress((err, addr) => {
address = addr;
if (self.isDev) {
return self.initDevChain((err) => {
next(err);
});
}
next();
return self.initChainAndGetAddress((err, addr) => {
address = addr;
next(err);
});
},
function getMainCommand(next) {
self.client.mainCommand(address, function(cmd, args) {
next(null, cmd, args);
}, true);
}
], function (err, cmd, args) {
], function(err, cmd, args) {
if (err) {
console.error(err.message);
return;
@ -177,7 +170,8 @@ Blockchain.prototype.run = function() {
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 = spawn(cmd, args, {cwd: process.cwd()});
self.child.on('error', (err) => {
err = err.toString();
console.error('Blockchain error: ', err);
@ -187,28 +181,20 @@ Blockchain.prototype.run = function() {
console.error(__('Otherwise, you can change your data directory in blockchain.json (datadir)').yellow);
}
});
// TOCHECK I don't understand why stderr and stdout are reverted.
// This happens with Geth and Parity, so it does not seems a client problem
self.child.stdout.on('data', (data) => {
console.error(`Geth error: ${data}`);
console.error(`${self.client.name} error: ${data}`);
});
let httpReady = false;
let wsReady = !self.config.wsRPC;
// Geth logs appear in stderr somehow
self.child.stderr.on('data', async (data) => {
data = data.toString();
if (data.indexOf('HTTP endpoint opened') > -1) {
httpReady = true;
}
if (data.indexOf('WebSocket endpoint opened') > -1) {
wsReady = true;
}
if (!self.readyCalled && wsReady && httpReady) {
if (!self.readyCalled && self.client.isReady(data)) {
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.fundAccounts((err) => {
if (err) console.error('Error funding accounts', err);
});
}
if (self.config.proxy) {
@ -216,36 +202,34 @@ Blockchain.prototype.run = function() {
}
self.readyCallback();
}
console.log('Geth: ' + data);
console.log(`${self.client.name}: ${data}`);
});
self.child.on('exit', (code) => {
let strCode;
if (code) {
strCode = ' with error code ' + code;
strCode = 'with error code ' + code;
} else {
strCode = ' with no error code (manually killed?)';
strCode = 'with no error code (manually killed?)';
}
console.error('Geth exited' + strCode);
if(self.onExitCallback){
console.error(self.client.name + ' exited ' + strCode);
if (self.onExitCallback) {
self.onExitCallback();
}
});
self.child.on('uncaughtException', (err) => {
console.error('Uncaught geth exception', err);
if(self.onExitCallback){
console.error('Uncaught ' + self.client.name + ' exception', err);
if (self.onExitCallback) {
self.onExitCallback();
}
});
});
};
Blockchain.prototype.createFundAndUnlockAccounts = function(cb) {
Blockchain.prototype.fundAccounts = function(cb) {
DevFunds.new({blockchainConfig: this.config}).then(devFunds => {
devFunds.createFundAndUnlockAccounts((err) => {
devFunds.fundAccounts(this.client.needKeepAlive(), (err) => {
cb(err);
});
});
@ -255,16 +239,13 @@ 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});
this.miner = this.client.getMiner();
}
};
Blockchain.prototype.kill = function() {
this.shutdownProxy();
if (this.child) {
this.child.kill();
}
@ -275,8 +256,8 @@ Blockchain.prototype.checkPathLength = function() {
if (dappPath.length > 66) {
// console.error is captured and sent to the console output regardless of silent setting
console.error("===============================================================================".yellow);
console.error("===========> ".yellow + __('WARNING! DApp path length is too long: ').yellow + dappPath.yellow);
console.error("===========> ".yellow + __('This is known to cause issues with starting geth, please consider reducing your DApp path\'s length to 66 characters or less.').yellow);
console.error("===========> ".yellow + __('WARNING! ÐApp path length is too long: ').yellow + dappPath.yellow);
console.error("===========> ".yellow + __('This is known to cause issues with starting geth, please consider reducing your ÐApp path\'s length to 66 characters or less.').yellow);
console.error("===============================================================================".yellow);
}
};
@ -285,12 +266,81 @@ Blockchain.prototype.isClientInstalled = function(callback) {
let versionCmd = this.client.determineVersionCommand();
this.runCommand(versionCmd, {}, (err, stdout, stderr) => {
if (err || !stdout || stderr.indexOf("not found") >= 0 || stdout.indexOf("not found") >= 0) {
return callback('Geth not found');
return callback(__('Ethereum client bin not found:') + ' ' + this.client.getBinaryPath());
}
callback();
});
};
Blockchain.prototype.initDevChain = function(callback) {
const self = this;
const ACCOUNTS_ALREADY_PRESENT = 'accounts_already_present';
// Init the dev chain
self.client.initDevChain('.embark/development/datadir', (err) => {
if (err) {
console.log(err);
return callback(err);
}
let needToCreateOtherAccounts = self.config.account && self.config.account.numAccounts;
if (!needToCreateOtherAccounts) return callback();
// Create other accounts
async.waterfall([
function listAccounts(next) {
self.runCommand(self.client.listAccountsCommand(), {}, (err, stdout, _stderr) => {
if (err || stdout === undefined || stdout.indexOf("Fatal") >= 0) {
console.log(__("no accounts found").green);
return next();
}
// List current addresses
self.config.unlockAddressList = self.client.parseListAccountsCommandResultToAddressList(stdout);
// Count current addresses and remove the default account from the count (because password can be different)
let addressCount = self.client.parseListAccountsCommandResultToAddressCount(stdout);
let utilityAddressCount = self.client.parseListAccountsCommandResultToAddressCount(stdout) - 1;
utilityAddressCount = (utilityAddressCount > 0 ? utilityAddressCount : 0);
let accountsToCreate = self.config.account.numAccounts - utilityAddressCount;
if (accountsToCreate > 0) {
next(null, accountsToCreate, addressCount === 0);
} else {
next(ACCOUNTS_ALREADY_PRESENT);
}
});
},
function newAccounts(accountsToCreate, firstAccount, next) {
// At first launch, Geth --dev does not create its own dev account if any other account are present. Parity --dev always does. Make this choerent between the two
if (firstAccount && self.client.name === 'geth') accountsToCreate++;
var accountNumber = 0;
async.whilst(
function() {
return accountNumber < accountsToCreate;
},
function(callback) {
accountNumber++;
self.runCommand(self.client.newAccountCommand(firstAccount), {}, (err, stdout, _stderr) => {
if (err) {
return callback(err, accountNumber);
}
self.config.unlockAddressList.push(self.client.parseNewAccountCommandResultToAddress(stdout));
firstAccount = false;
callback(null, accountNumber);
});
},
function(err) {
next(err);
}
);
}
], (err) => {
if (err && err !== ACCOUNTS_ALREADY_PRESENT) {
console.log(err);
return callback(err);
}
callback();
});
});
};
Blockchain.prototype.initChainAndGetAddress = function(callback) {
const self = this;
let address = null;
@ -307,17 +357,23 @@ Blockchain.prototype.initChainAndGetAddress = function(callback) {
},
function listAccounts(next) {
self.runCommand(self.client.listAccountsCommand(), {}, (err, stdout, _stderr) => {
if (err || stdout === undefined || stdout.match(/{(\w+)}/) === null || stdout.indexOf("Fatal") >= 0) {
if (err || stdout === undefined || stdout.indexOf("Fatal") >= 0) {
console.log(__("no accounts found").green);
return next();
}
let firstAccountFound = self.client.parseListAccountsCommandResultToAddress(stdout);
if (firstAccountFound === undefined || firstAccountFound === "") {
console.log(__("no accounts found").green);
return next();
}
console.log(__("already initialized").green);
address = stdout.match(/{(\w+)}/)[1];
address = firstAccountFound;
next(ALREADY_INITIALIZED);
});
},
function genesisBlock(next) {
if (!self.config.genesisBlock) {
//There's no genesis init with Parity. Custom network are set in the chain property at startup
if (!self.config.genesisBlock || self.client.name === 'parity') {
return next();
}
console.log(__("initializing genesis block").green);
@ -330,7 +386,7 @@ Blockchain.prototype.initChainAndGetAddress = function(callback) {
if (err) {
return next(err);
}
address = stdout.match(/{(\w+)}/)[1];
address = self.client.parseNewAccountCommandResultToAddress(stdout);
next();
});
}
@ -342,13 +398,34 @@ Blockchain.prototype.initChainAndGetAddress = function(callback) {
});
};
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, onExitCallback});
var BlockchainClient = function(userConfig, clientName, env, onReadyCallback, onExitCallback) {
if ((userConfig === {} || JSON.stringify(userConfig) === '{"enabled":true}') && env !== 'development') {
console.log("===> " + __("warning: running default config on a non-development environment"));
}
throw new Error('unknown client');
// if client is not set in preferences, default is geth
if (!userConfig.ethereumClientName) userConfig.ethereumClientName = 'geth';
// if clientName is set, it overrides preferences
if (clientName) userConfig.ethereumClientName = clientName;
// Choose correct client instance based on clientName
let clientClass;
switch (userConfig.ethereumClientName) {
case 'geth':
clientClass = GethClient;
break;
case 'parity':
clientClass = ParityClient;
break;
default:
console.error(__('Unknow client "%s". Please use one of the following: %s', userConfig.ethereumClientName, 'geth, parity'));
process.exit();
}
userConfig.isDev = (userConfig.isDev || userConfig.default);
userConfig.env = env;
userConfig.onReadyCallback = onReadyCallback;
userConfig.onExitCallback = onExitCallback;
return new Blockchain(userConfig, clientClass);
};
module.exports = BlockchainClient;

View File

@ -32,7 +32,7 @@ class BlockchainProcess extends ProcessWrapper {
}
blockchainExit() {
// tell our parent process that geth has exited
// tell our parent process that ethereum client has exited
blockchainProcess.send({result: constants.blockchain.blockchainExit});
}

View File

@ -11,6 +11,7 @@ class BlockchainProcessLauncher {
this.blockchainConfig = options.blockchainConfig;
this.locale = options.locale;
this.isDev = options.isDev;
this.client = options.client;
}
processEnded(code) {
@ -30,9 +31,7 @@ class BlockchainProcessLauncher {
this.blockchainProcess.send({
action: constants.blockchain.init, options: {
blockchainConfig: this.blockchainConfig,
//client: this.client,
// TODO: assume for now it's geth
client: 'geth',
client: this.client,
env: this.env,
isDev: this.isDev,
locale: this.locale

View File

@ -4,28 +4,28 @@ const {getWeiBalanceFromString, buildUrl} = require('../../utils/utils.js');
const {readFileSync, dappPath} = require('../../core/fs');
class DevFunds {
constructor(options) {
constructor(options) {
this.blockchainConfig = options.blockchainConfig;
this.accounts = [];
this.numAccounts = this.blockchainConfig.account.numAccounts || 0;
this.password = this.blockchainConfig.account.password ? readFileSync(dappPath(this.blockchainConfig.account.password), 'utf8').replace('\n', '') : 'dev_password';
this.networkId = null;
this.balance = Web3.utils.toWei("1", "ether");
this.provider = options.provider || new Web3.providers.WebsocketProvider(buildUrl('ws', this.blockchainConfig.wsHost, this.blockchainConfig.wsPort), {headers: {Origin: "http://localhost:8000"}});
this.provider = options.provider || new Web3.providers.WebsocketProvider(buildUrl('ws', this.blockchainConfig.wsHost, this.blockchainConfig.wsPort), {headers: {Origin: "http://embark"}});
this.web3 = new Web3(this.provider);
if (this.blockchainConfig.account.balance) {
this.balance = getWeiBalanceFromString(this.blockchainConfig.account.balance, this.web3);
}
this.logger = options.logger || console;
}
static async new(options){
static async new(options) {
const df = new DevFunds(options);
await df._init();
return df;
}
async _init () {
async _init() {
const accounts = await this.web3.eth.getAccounts();
this.web3.eth.defaultAccount = accounts[0];
if (accounts.length > 1) {
@ -34,13 +34,13 @@ class DevFunds {
}
_sendTx() {
if (this.networkId !== 1337) {
// Send TXs only in dev networks
if (this.networkId !== 1337 && this.networkId !== 17) {
return;
}
this.web3.eth.sendTransaction({value: "1000000000000000", to: "0xA2817254cb8E7b6269D1689c3E0eBadbB78889d1", from: this.web3.eth.defaultAccount});
}
// trigger regular txs due to a bug in geth and stuck transactions in --dev mode
_regularTxs(cb) {
const self = this;
self.web3.eth.net.getId().then((networkId) => {
@ -48,23 +48,30 @@ class DevFunds {
if (self.networkId !== 1337) {
return;
}
setInterval(function () { self._sendTx(); }, 1500);
setInterval(function() { self._sendTx(); }, 1500);
if (cb) {
cb();
}
});
}
_regularUnlocks() {
const self = this;
setInterval(function () { self.unlockAccounts(self.password, () => { }); }, 20000);
_fundAccounts(balance, cb) {
async.each(this.accounts, (account, next) => {
this.web3.eth.getBalance(account).then(currBalance => {
const remainingBalance = balance - currBalance;
if (remainingBalance <= 0) return next();
this.web3.eth.sendTransaction({to: account, value: remainingBalance}).catch(console.error);
next(); // don't wait for the tx receipt as it never comes!
}).catch(console.error);
}, cb);
}
// TOCHECK:
// This function is not used anymore, but I leave because it is used for testing purpose (see test/devFunds.js)
// I suggest to remove this function and the corresponding test case
createAccounts(numAccounts, password, cb) {
const numAccountsToCreate = numAccounts - (this.accounts.length + 1);
if (numAccountsToCreate === 0) return cb();
async.timesLimit(numAccountsToCreate, 1, (_, next) => {
this.web3.eth.personal.newAccount(password, next);
}, (err, accounts) => {
@ -74,6 +81,9 @@ class DevFunds {
});
}
// TOCHECK:
// This function is not used anymore, but I leave because it is used for testing purpose (see test/devFunds.js)
// I suggest to remove this function and the corresponding test case
unlockAccounts(password, cb) {
async.each(this.accounts, (account, next) => {
this.web3.eth.personal.unlockAccount(account, password).then((_result) => {
@ -82,33 +92,14 @@ class DevFunds {
}, cb);
}
fundAccounts(balance, cb) {
async.each(this.accounts, (account, next) => {
this.web3.eth.getBalance(account).then(currBalance => {
const remainingBalance = balance - currBalance;
if (remainingBalance <= 0) return next();
this.web3.eth.sendTransaction({to: account, value: remainingBalance}).catch(next);
next(); // don't wait for the tx receipt as it never comes!
}).catch(cb);
}, cb);
}
createFundAndUnlockAccounts(cb) {
fundAccounts(pingForever = false, cb) {
if (!this.web3) {
return cb();
}
async.waterfall([
(next) => {
this.createAccounts(this.numAccounts, this.password, next);
},
(next) => {
this.unlockAccounts(this.password, next);
},
(next) => {
this._regularTxs();
this._regularUnlocks();
this.fundAccounts(this.balance, next);
if (pingForever) this._regularTxs();
this._fundAccounts(this.balance, next);
}
], cb);
}

View File

@ -1,16 +1,59 @@
const async = require('async');
const GethMiner = require('./miner');
const os = require('os');
const DEFAULTS = {
"BIN": "geth",
"NETWORK_TYPE": "custom",
"NETWORK_ID": 1337,
"RPC_API": ['eth', 'web3', 'net', 'debug'],
"WS_API": ['eth', 'web3', 'net', 'shh', 'debug', 'pubsub'],
"DEV_WS_API": ['eth', 'web3', 'net', 'shh', 'debug', 'pubsub', 'personal'],
"TARGET_GAS_LIMIT": 8000000
};
// TODO: make all of this async
class GethCommands {
class GethClient {
static get DEFAULTS() {
return DEFAULTS;
}
constructor(options) {
this.config = options && options.hasOwnProperty('config') ? options.config : {};
this.env = options && options.hasOwnProperty('env') ? options.env : 'development';
this.isDev = options && options.hasOwnProperty('isDev') ? options.isDev : (this.env === 'development');
this.name = "Go-Ethereum (https://github.com/ethereum/go-ethereum)";
this.geth_bin = this.config.geth_bin || "geth";
this.name = "geth";
this.prettyName = "Go-Ethereum (https://github.com/ethereum/go-ethereum)";
this.bin = this.config.ethereumClientBin || DEFAULTS.BIN;
this.httpReady = false;
this.wsReady = !this.config.wsRPC;
}
commonOptions() {
isReady(data) {
if (data.indexOf('HTTP endpoint opened') > -1) {
this.httpReady = true;
}
if (data.indexOf('WebSocket endpoint opened') > -1) {
this.wsReady = true;
}
return this.httpReady && this.wsReady;
}
/**
* Check if the client needs some sort of 'keep alive' transactions to avoid freezing by inactivity
* @returns {boolean} if keep alive is needed
*/
needKeepAlive() {
// TODO: check version also (geth version < 1.8.15)
if (this.isDev) {
// Trigger regular txs due to a bug in geth (< 1.8.15) and stuck transactions in --dev mode.
return true;
}
return false;
}
commonOptions(firstAccount = false) {
let config = this.config;
let cmd = [];
@ -20,23 +63,33 @@ class GethCommands {
cmd.push(`--datadir=${config.datadir}`);
}
if (config.syncMode || config.syncmode) {
cmd.push("--syncmode=" + (config.syncmode || config.syncMode));
if (config.syncMode) {
cmd.push("--syncmode=" + config.syncMode);
}
// geth in dev mode needs the first account to have a blank password, so we use for convenience the same Parity's devpassword
if (config.account && config.account.password) {
cmd.push(`--password=${config.account.password}`);
if (firstAccount) cmd.push(`--password=${config.account.devPassword}`);
else cmd.push(`--password=${config.account.password}`);
}
if (Number.isInteger(config.verbosity) && config.verbosity >=0 && config.verbosity <= 5) {
if (Number.isInteger(config.verbosity) && config.verbosity >= 0 && config.verbosity <= 5) {
cmd.push("--verbosity=" + config.verbosity);
}
return cmd;
}
getMiner() {
return new GethMiner({datadir: this.config.datadir});
}
getBinaryPath() {
return this.bin;
}
determineVersionCommand() {
return this.geth_bin + " version";
return this.bin + " version";
}
determineNetworkType(config) {
@ -53,89 +106,101 @@ class GethCommands {
initGenesisCommmand() {
let config = this.config;
let cmd = this.geth_bin + " " + this.commonOptions().join(' ');
let cmd = this.bin + " " + this.commonOptions().join(' ');
if (config.genesisBlock) {
cmd += " init \"" + config.genesisBlock + "\" ";
}
return cmd;
}
newAccountCommand() {
if (!(this.config.account && this.config.account.password)){
newAccountCommand(firstAccount = false) {
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 ";
return this.bin + " " + this.commonOptions(firstAccount).join(' ') + " account new ";
}
parseNewAccountCommandResultToAddress(data = "") {
if (data.match(/{(\w+)}/)) return "0x" + data.match(/{(\w+)}/)[1];
return "";
}
listAccountsCommand() {
return this.geth_bin + " " + this.commonOptions().join(' ') + " account list ";
return this.bin + " " + this.commonOptions().join(' ') + " account list ";
}
parseListAccountsCommandResultToAddress(data = "") {
if (data.match(/{(\w+)}/)) return "0x" + data.match(/{(\w+)}/)[1];
return "";
}
parseListAccountsCommandResultToAddressList(data = "") {
let list = data.split(os.EOL);
list.pop(); // Remove empty value
return list.map(el => "0x" + el.match(/{(\w+)}/)[1]);
}
parseListAccountsCommandResultToAddressCount(data = "") {
const count = this.parseListAccountsCommandResultToAddressList(data).length;
return (count > 0 ? count : 0);
}
determineRpcOptions(config) {
let cmd = [];
cmd.push("--port=" + config.port);
cmd.push("--rpc");
cmd.push("--rpcport=" + config.rpcPort);
cmd.push("--rpcaddr=" + config.rpcHost);
if (config.rpcCorsDomain) {
if (config.rpcCorsDomain === '*') {
console.log('==================================');
console.log(__('rpcCorsDomain set to *'));
console.log(__('make sure you know what you are doing'));
console.log('==================================');
console.warn('==================================');
console.warn(__('rpcCorsDomain set to *'));
console.warn(__('make sure you know what you are doing'));
console.warn('==================================');
}
cmd.push("--rpccorsdomain=" + config.rpcCorsDomain);
} else {
console.log('==================================');
console.log(__('warning: cors is not set'));
console.log('==================================');
console.warn('==================================');
console.warn(__('warning: cors is not set'));
console.warn('==================================');
}
return cmd;
}
determineWsOptions(config) {
let cmd = [];
if (config.wsRPC) {
cmd.push("--ws");
cmd.push("--wsport=" + config.wsPort);
cmd.push("--wsaddr=" + config.wsHost);
if (config.wsOrigins) {
if (config.wsOrigins === '*') {
console.log('==================================');
console.log(__('wsOrigins set to *'));
console.log(__('make sure you know what you are doing'));
console.log('==================================');
console.warn('==================================');
console.warn(__('wsOrigins set to *'));
console.warn(__('make sure you know what you are doing'));
console.warn('==================================');
}
cmd.push("--wsorigins=" + config.wsOrigins);
} else {
console.log('==================================');
console.log(__('warning: wsOrigins is not set'));
console.log('==================================');
console.warn('==================================');
console.warn(__('warning: wsOrigins is not set'));
console.warn('==================================');
}
}
return cmd;
}
initDevChain(datadir, callback) {
// No specific configuration needed for the dev chain
return callback();
}
mainCommand(address, done) {
let self = this;
let config = this.config;
let rpc_api = (this.config.rpcApi || ['eth', 'web3', 'net', 'debug']);
let defaultWsApi = ['eth', 'web3', 'net', 'shh', 'debug'];
if (this.isDev) {
defaultWsApi.push('personal');
}
let ws_api = (this.config.wsApi || defaultWsApi);
let rpc_api = this.config.rpcApi;
let ws_api = this.config.wsApi;
let args = [];
async.series([
function commonOptions(callback) {
let cmd = self.commonOptions();
@ -205,13 +270,18 @@ class GethCommands {
callback(null, '--wsapi=' + ws_api.join(','));
},
function accountToUnlock(callback) {
if (self.isDev && self.config.unlockAddressList) {
// The first address is the dev account, that is automatically unlocked by the client using blank password
args.push("--unlock=" + self.config.unlockAddressList.slice(1));
return callback(null, "--unlock=" + self.config.unlockAddressList.slice(1));
}
let accountAddress = "";
if(config.account && config.account.address) {
if (config.account && config.account.address) {
accountAddress = config.account.address;
} else {
accountAddress = address;
}
if (accountAddress && !self.isDev) {
if (accountAddress) {
args.push("--unlock=" + accountAddress);
return callback(null, "--unlock=" + accountAddress);
}
@ -219,8 +289,8 @@ class GethCommands {
},
function gasLimit(callback) {
if (config.targetGasLimit) {
args.push("--targetgaslimit=" + config.targetGasLimit);
return callback(null, "--targetgaslimit=" + config.targetGasLimit);
args.push("--miner.gastarget=" + config.targetGasLimit);
return callback(null, "--miner.gastarget=" + config.targetGasLimit);
}
callback(null, "");
},
@ -231,13 +301,13 @@ class GethCommands {
}
callback(null, '');
}
], function (err) {
], function(err) {
if (err) {
throw new Error(err.message);
}
return done(self.geth_bin, args);
return done(self.bin, args);
});
}
}
module.exports = GethCommands;
module.exports = GethClient;

View File

@ -14,6 +14,7 @@ class BlockchainModule {
this.locale = options.locale;
this.isDev = options.isDev;
this.ipc = options.ipc;
this.client = options.client;
this.registerBlockchainProcess();
}
@ -87,7 +88,7 @@ class BlockchainModule {
const {host, port, type, protocol} = self.contractsConfig.deployment;
utils.pingEndpoint(host, port, type, protocol, self.blockchainConfig.wsOrigins.split(',')[0], next);
}
], function (err) {
], function(err) {
if (err === true || err === undefined) {
return cb(true);
}
@ -97,13 +98,15 @@ class BlockchainModule {
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
isDev: self.isDev,
client: self.client
});
blockchainProcess.startBlockchainNode();

View File

@ -0,0 +1,345 @@
const async = require('async');
const fs = require('../../core/fs.js');
const os = require('os');
const DEFAULTS = {
"BIN": "parity",
"NETWORK_TYPE": "dev",
"NETWORK_ID": 17,
"RPC_API": ["web3", "eth", "pubsub", "net", "parity", "private", "parity_pubsub", "traces", "rpc", "shh", "shh_pubsub"],
"WS_API": ["web3", "eth", "pubsub", "net", "parity", "private", "parity_pubsub", "traces", "rpc", "shh", "shh_pubsub"],
"DEV_WS_API": ["web3", "eth", "pubsub", "net", "parity", "private", "parity_pubsub", "traces", "rpc", "shh", "shh_pubsub", "personal"],
"TARGET_GAS_LIMIT": 8000000,
"DEV_ACCOUNT": "0x00a329c0648769a73afac7f9381e08fb43dbea72",
"DEV_WALLET": {"id": "d9460e00-6895-8f58-f40c-bb57aebe6c00", "version": 3, "crypto": {"cipher": "aes-128-ctr", "cipherparams": {"iv": "74245f453143f9d06a095c6e6e309d5d"}, "ciphertext": "2fa611c4aa66452ef81bd1bd288f9d1ed14edf61aa68fc518093f97c791cf719", "kdf": "pbkdf2", "kdfparams": {"c": 10240, "dklen": 32, "prf": "hmac-sha256", "salt": "73b74e437a1144eb9a775e196f450a23ab415ce2c17083c225ddbb725f279b98"}, "mac": "f5882ae121e4597bd133136bf15dcbcc1bb2417a25ad205041a50c59def812a8"}, "address": "00a329c0648769a73afac7f9381e08fb43dbea72", "name": "Development Account", "meta": "{\"description\":\"Never use this account outside of development chain!\",\"passwordHint\":\"Password is empty string\"}"}
};
const safePush = function(set, value) {
if (set.indexOf(value) === -1) {
set.push(value);
}
};
class ParityClient {
static get DEFAULTS() {
return DEFAULTS;
}
constructor(options) {
this.config = options && options.hasOwnProperty('config') ? options.config : {};
this.env = options && options.hasOwnProperty('env') ? options.env : 'development';
this.isDev = options && options.hasOwnProperty('isDev') ? options.isDev : (this.env === 'development');
this.name = "parity";
this.prettyName = "Parity-Ethereum (https://github.com/paritytech/parity-ethereum)";
this.bin = this.config.ethereumClientBin || DEFAULTS.BIN;
}
isReady(data) {
return data.indexOf('Public node URL') > -1;
}
/**
* Check if the client needs some sort of 'keep alive' transactions to avoid freezing by inactivity
* @returns {boolean} if keep alive is needed
*/
needKeepAlive() {
return false;
}
commonOptions() {
let config = this.config;
let cmd = [];
cmd.push(this.determineNetworkType(config));
if (config.networkId) {
cmd.push(`--network-id=${config.networkId}`);
}
if (config.datadir) {
cmd.push(`--base-path=${config.datadir}`);
}
if (config.syncMode === 'light') {
cmd.push("--light");
} else if (config.syncMode === 'fast') {
cmd.push("--pruning=fast");
} else if (config.syncMode === 'full') {
cmd.push("--pruning=archive");
}
// In dev mode we store all users passwords in the devPassword file, so Parity can unlock all users from the start
if (this.isDev) cmd.push(`--password=${config.account.devPassword}`);
else if (config.account && config.account.password) {
cmd.push(`--password=${config.account.password}`);
}
if (Number.isInteger(config.verbosity) && config.verbosity >= 0 && config.verbosity <= 5) {
switch (config.verbosity) {
case 0: // No option to silent Parity, go to less verbose
case 1:
cmd.push("--logging=error");
break;
case 2:
cmd.push("--logging=warn");
break;
case 3:
cmd.push("--logging=info");
break;
case 4: // Debug is the max verbosity for Parity
case 5:
cmd.push("--logging=debug");
break;
default:
cmd.push("--logging=info");
break;
}
}
return cmd;
}
getMiner() {
console.warn(__("Miner requested, but Parity does not embed a miner! Use Geth or install ethminer (https://github.com/ethereum-mining/ethminer)").yellow);
return;
}
getBinaryPath() {
return this.bin;
}
determineVersionCommand() {
return this.bin + " --version";
}
determineNetworkType(config) {
if (this.isDev) {
return "--chain=dev";
}
if (config.networkType === 'rinkeby') {
console.warn(__('Parity does not support the Rinkeby PoA network, switching to Kovan PoA network'));
config.networkType = 'kovan';
} else if (config.networkType === 'testnet') {
console.warn(__('Parity "testnet" corresponds to Kovan network, switching to Ropsten to be compliant with Geth parameters'));
config.networkType = "ropsten";
}
if (config.genesisBlock) {
config.networkType = config.genesisBlock;
}
return "--chain=" + config.networkType;
}
newAccountCommand() {
return this.bin + " " + this.commonOptions().join(' ') + " account new ";
}
parseNewAccountCommandResultToAddress(data = "") {
return data.replace(/^\n|\n$/g, "");
}
listAccountsCommand() {
return this.bin + " " + this.commonOptions().join(' ') + " account list ";
}
parseListAccountsCommandResultToAddress(data = "") {
return data.replace(/^\n|\n$/g, "").split('\n')[0];
}
parseListAccountsCommandResultToAddressList(data = "") {
let list = data.split(os.EOL);
list.pop();
return list;
}
parseListAccountsCommandResultToAddressCount(data = "") {
const count = this.parseListAccountsCommandResultToAddressList(data).length;
return (count > 0 ? count : 0);
}
determineRpcOptions(config) {
let cmd = [];
cmd.push("--port=" + config.port);
cmd.push("--jsonrpc-port=" + config.rpcPort);
cmd.push("--jsonrpc-interface=" + (config.rpcHost === 'localhost' ? 'local' : config.rpcHost));
if (config.rpcCorsDomain) {
if (config.rpcCorsDomain === '*') {
console.warn('==================================');
console.warn(__('rpcCorsDomain set to "all"'));
console.warn(__('make sure you know what you are doing'));
console.warn('==================================');
}
cmd.push("--jsonrpc-cors=" + (config.rpcCorsDomain === '*' ? 'all' : config.rpcCorsDomain));
} else {
console.warn('==================================');
console.warn(__('warning: cors is not set'));
console.warn('==================================');
}
cmd.push("--jsonrpc-hosts=all");
return cmd;
}
determineWsOptions(config) {
let cmd = [];
if (config.wsRPC) {
cmd.push("--ws-port=" + config.wsPort);
cmd.push("--ws-interface=" + (config.wsHost === 'localhost' ? 'local' : config.wsHost));
if (config.wsOrigins) {
if (config.wsOrigins === '*') {
console.warn('==================================');
console.warn(__('wsOrigins set to "all"'));
console.warn(__('make sure you know what you are doing'));
console.warn('==================================');
}
cmd.push("--ws-origins=" + (config.wsOrigins === '*' ? 'all' : config.wsOrigins));
} else {
console.warn('==================================');
console.warn(__('warning: wsOrigins is not set'));
console.warn('==================================');
}
cmd.push("--ws-hosts=all");
}
return cmd;
}
initDevChain(datadir, callback) {
// Parity requires specific initialization also for the dev chain
const self = this;
const keysDataDir = '.embark/development/datadir/keys/DevelopmentChain';
async.series([
function makeDir(next) {
fs.mkdirp(keysDataDir, (err, _result) => {
next(err);
});
},
function createDevAccount(next) {
self.createDevAccount(keysDataDir, next);
},
function updatePasswordFile(next) {
if (self.config.account.password) {
let passwordList = os.EOL + (self.config.account.password ? fs.readFileSync(fs.dappPath(self.config.account.password), 'utf8').replace('\n', '') : 'dev_password');
fs.writeFileSync(self.config.account.devPassword, passwordList, function(err) {
return next(err);
});
}
next();
}
], (err) => {
callback(err);
});
}
createDevAccount(keysDataDir, cb) {
const devAccountWallet = keysDataDir + '/dev.wallet';
fs.writeFile(devAccountWallet, JSON.stringify(DEFAULTS.DEV_WALLET), function(err) {
if (err) {
return cb(err);
}
cb();
});
}
mainCommand(address, done) {
let self = this;
let config = this.config;
let rpc_api = this.config.rpcApi;
let ws_api = this.config.wsApi;
let args = [];
async.series([
function commonOptions(callback) {
let cmd = self.commonOptions();
args = args.concat(cmd);
callback(null, cmd);
},
function rpcOptions(callback) {
let cmd = self.determineRpcOptions(self.config);
args = args.concat(cmd);
callback(null, cmd);
},
function wsOptions(callback) {
let cmd = self.determineWsOptions(self.config);
args = args.concat(cmd);
callback(null, cmd);
},
function dontGetPeers(callback) {
if (config.nodiscover) {
args.push("--no-discovery");
return callback(null, "--no-discovery");
}
callback(null, "");
},
function vmDebug(callback) {
if (config.vmdebug) {
args.push("--tracing on");
return callback(null, "--tracing on");
}
callback(null, "");
},
function maxPeers(callback) {
let cmd = "--max-peers=" + config.maxpeers;
args.push(cmd);
callback(null, cmd);
},
function bootnodes(callback) {
if (config.bootnodes && config.bootnodes !== "" && config.bootnodes !== []) {
args.push("--bootnodes=" + config.bootnodes);
return callback(null, "--bootnodes=" + config.bootnodes);
}
callback("");
},
function whisper(callback) {
if (config.whisper) {
safePush(rpc_api, 'shh');
safePush(rpc_api, 'shh_pubsub');
safePush(ws_api, 'shh');
safePush(ws_api, 'shh_pubsub');
args.push("--whisper");
return callback(null, "--whisper");
}
callback("");
},
function rpcApi(callback) {
args.push('--jsonrpc-apis=' + rpc_api.join(','));
callback(null, '--jsonrpc-apis=' + rpc_api.join(','));
},
function wsApi(callback) {
args.push('--ws-apis=' + ws_api.join(','));
callback(null, '--ws-apis=' + ws_api.join(','));
},
function accountToUnlock(callback) {
if (self.isDev) {
let unlockAddressList = self.config.unlockAddressList ? self.config.unlockAddressList : DEFAULTS.DEV_ACCOUNT;
args.push("--unlock=" + unlockAddressList);
return callback(null, "--unlock=" + unlockAddressList);
}
let accountAddress = "";
if (config.account && config.account.address) {
accountAddress = config.account.address;
} else {
accountAddress = address;
}
if (accountAddress && !self.isDev) {
args.push("--unlock=" + accountAddress);
return callback(null, "--unlock=" + accountAddress);
}
callback(null, "");
},
function gasLimit(callback) {
if (config.targetGasLimit) {
args.push("--gas-floor-target=" + config.targetGasLimit);
return callback(null, "--gas-floor-target=" + config.targetGasLimit);
}
// Default Parity gas limit is 4700000: let's set to the geth default
args.push("--gas-floor-target=" + DEFAULTS.TARGET_GAS_LIMIT);
return callback(null, "--gas-floor-target=" + DEFAULTS.TARGET_GAS_LIMIT);
}
], function(err) {
if (err) {
throw new Error(err.message);
}
return done(self.bin, args);
});
}
}
module.exports = ParityClient;

View File

@ -26,20 +26,42 @@ class Whisper {
connectToProvider() {
let {host, port} = this.communicationConfig.connection;
let web3Endpoint = 'ws://' + host + ':' + port;
this.web3.setProvider(new Web3.providers.WebsocketProvider(web3Endpoint, {headers: {Origin: "embark"}}));
// Note: dont't pass to the provider things like {headers: {Origin: "embark"}}. Origin header is for browser to fill
// to protect user, it has no meaning if it is used server-side. See here for more details: https://github.com/ethereum/go-ethereum/issues/16608
// Moreover, Parity reject origins that are not urls so if you try to connect with Origin: "embark" it gives the followin error:
// << Blocked connection to WebSockets server from untrusted origin: Some("embark") >>
// The best choice is to use void origin, BUT Geth rejects void origin, so to keep both clients happy we can use http://embark
this.web3.setProvider(new Web3.providers.WebsocketProvider(web3Endpoint, {headers: {Origin: "http://embark"}}));
}
setServiceCheck() {
const self = this;
self.events.request("services:register", 'Whisper', function (cb) {
self.events.request("services:register", 'Whisper', function(cb) {
if (!self.web3.currentProvider || self.web3.currentProvider.connection.readyState !== 1) {
return self.connectToProvider();
}
self.web3.shh.getVersion(function (err, version) {
if (err || version == "2") {
return cb({name: 'Whisper', status: 'off'});
}
return cb({name: 'Whisper (version ' + version + ')', status: 'on'});
// 1) Parity does not implement shh_version JSON-RPC method
// 2) web3 1.0 still does not implement web3_clientVersion
// so we must do all by our own
self.web3._requestManager.send({method: 'web3_clientVersion', params: []}, (err, clientVersion) => {
if (err) return cb(err);
if (clientVersion.indexOf("Parity-Ethereum//v2") === 0) {
// This is Parity
return self.web3.shh.getInfo(function(err) {
if (err) {
return cb({name: 'Whisper', status: 'off'});
}
// TOFIX Assume Whisper v6 until there's a way to understand it via JSON-RPC
return cb({name: 'Whisper (version 6)', status: 'on'});
});
}
// Assume it is a Geth compliant client
self.web3.shh.getVersion(function(err, version) {
if (err || version == "2") {
return cb({name: 'Whisper', status: 'off'});
}
return cb({name: 'Whisper (version ' + version + ')', status: 'on'});
});
});
});
}
@ -85,7 +107,7 @@ class Whisper {
const code = `\nEmbarkJS.Messages.setProvider('whisper', ${JSON.stringify(config)});`;
this.embark.addProviderInit('communication', code, shouldInit);
const consoleConfig = Object.assign({}, config, {providerOptions: {headers: {Origin: "embark"}}});
const consoleConfig = Object.assign({}, config, {providerOptions: {headers: {Origin: "http://embark"}}});
const consoleCode = `\nEmbarkJS.Messages.setProvider('whisper', ${JSON.stringify(consoleConfig)});`;
this.embark.addConsoleProviderInit('communication', consoleCode, shouldInit);
}

View File

@ -3,7 +3,7 @@
// for the whisper v5 and web3.js 1.0
let __embarkWhisperNewWeb3 = {};
__embarkWhisperNewWeb3.setProvider = function (options) {
__embarkWhisperNewWeb3.setProvider = function(options) {
const self = this;
let provider;
if (options === undefined) {
@ -14,7 +14,7 @@ __embarkWhisperNewWeb3.setProvider = function (options) {
// TODO: take into account type
self.web3 = new Web3(new Web3.providers.WebsocketProvider("ws://" + provider, options.providerOptions));
self.web3.currentProvider.on('connect', () => {
self.getWhisperVersion(function (err, version) {
self.getWhisperVersion(function(err, version) {
if (err) {
console.log("whisper not available");
} else if (version >= 5) {
@ -35,7 +35,7 @@ __embarkWhisperNewWeb3.setProvider = function (options) {
});
};
__embarkWhisperNewWeb3.sendMessage = function (options) {
__embarkWhisperNewWeb3.sendMessage = function(options) {
var topics, data, ttl, payload;
topics = options.topic;
data = options.data || options.payload;
@ -67,7 +67,7 @@ __embarkWhisperNewWeb3.sendMessage = function (options) {
if (options.pubKey) {
message.pubKey = options.pubKey; // encrypt using a given pubKey
} else if(options.symKeyID) {
} else if (options.symKeyID) {
message.symKeyID = options.symKeyID; // encrypts using given sym key ID
} else {
message.symKeyID = this.symKeyID; // encrypts using the sym key ID
@ -77,18 +77,18 @@ __embarkWhisperNewWeb3.sendMessage = function (options) {
throw new Error("missing option: topic");
}
this.web3.shh.post(message, function () {
this.web3.shh.post(message, function() {
});
};
__embarkWhisperNewWeb3.listenTo = function (options, callback) {
__embarkWhisperNewWeb3.listenTo = function(options, callback) {
var topics = options.topic;
let promise = new __MessageEvents();
let subOptions = {};
if(topics){
if (topics) {
if (typeof topics === 'string') {
topics = [this.web3.utils.toHex(topics).slice(0, 10)];
} else {
@ -116,34 +116,53 @@ __embarkWhisperNewWeb3.listenTo = function (options, callback) {
}
let filter = this.web3.shh.subscribe("messages", subOptions)
.on('data', function (result) {
var payload = JSON.parse(EmbarkJS.Utils.toAscii(result.payload));
var data;
data = {
topic: EmbarkJS.Utils.toAscii(result.topic),
data: payload,
//from: result.from,
time: result.timestamp
};
.on('data', function(result) {
var payload = JSON.parse(EmbarkJS.Utils.toAscii(result.payload));
var data;
data = {
topic: EmbarkJS.Utils.toAscii(result.topic),
data: payload,
//from: result.from,
time: result.timestamp
};
if (callback) {
return callback(null, data);
}
promise.cb(payload, data, result);
});
if (callback) {
return callback(null, data);
}
promise.cb(payload, data, result);
});
promise.filter = filter;
return promise;
};
__embarkWhisperNewWeb3.getWhisperVersion = function (cb) {
this.web3.shh.getVersion(function (err, version) {
cb(err, version);
__embarkWhisperNewWeb3.getWhisperVersion = function(cb) {
// 1) Parity does not implement shh_version JSON-RPC method
// 2) web3 1.0 still does not implement web3_clientVersion
// so we must do all by our own
const self = this;
self.web3._requestManager.send({method: 'web3_clientVersion', params: []}, (err, clientVersion) => {
if (err) return cb(err);
if (clientVersion.indexOf("Parity-Ethereum//v2") === 0) {
// This is Parity
self.web3.shh.getInfo(function(err) {
if (err) {
return cb(err, 0);
}
// TOFIX Assume Whisper v6 until there's a way to understand it via JSON-RPC
return cb(err, 6);
});
} else {
// Assume it is a Geth compliant client
self.web3.shh.getVersion(function(err, version) {
cb(err, version);
});
}
});
};
__embarkWhisperNewWeb3.isAvailable = function () {
__embarkWhisperNewWeb3.isAvailable = function() {
return new Promise((resolve, reject) => {
if (!this.web3.shh) {
return resolve(false);

View File

@ -14,7 +14,9 @@
"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)",
"Use a specific ethereum client [%s] (default: %s)": "Use a specific ethereum client [%s] (default: %s)",
"Unknow client \"%s\". Please use one of the following: %s": "Unknow client \"%s\". Please use one of the following: %s",
"Miner requested, but Parity does not embed a miner! Use Geth or install ethminer (https://github.com/ethereum-mining/ethminer)":"Miner requested, but Parity does not embed a miner! Use Geth or install ethminer (https://github.com/ethereum-mining/ethminer)",
"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]",
@ -123,7 +125,6 @@
"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...",
@ -144,6 +145,7 @@
"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.",
"Ethereum client bin not found:":"Ethereum client bin not found:",
"%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",

View File

@ -76,7 +76,7 @@
"unknown command": "comando desconocido",
"did you mean": "quiso decir",
"warning: running default config on a non-development environment": "precaución: ejecutando la configuración predeterminada 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}}; ¿Se encuentra {{client_name}} instalado o en la ruta (PATH)?",
"Ethereum client bin not found:":"Ethereum client bin not found:",
"no accounts found": "no se encontraron cuentas",
"initializing genesis block": "inicializando bloque genesis",
"rpcCorsDomain set to *": "rpcCorsDomain definido como *",

View File

@ -18,14 +18,14 @@
"filename to output logs (default: none)": "nom de fichier pour les journaux de sortie (par défaut: aucun)",
"level of logging to display": "niveau de journalisation à afficher",
"deploy and build dapp at ": "Publiez et créez la dapp dans ",
"port to run the dev webserver (default: %s)": "port pour exécuter le serveur de développement Web (par défaut:% s)",
"host to run the dev webserver (default: %s)": "hôte pour exécuter le serveur de développement Web (par défaut:% s)",
"port to run the dev webserver (default: %s)": "port pour exécuter le serveur de développement Web (par défaut: %s)",
"host to run the dev webserver (default: %s)": "hôte pour exécuter le serveur de développement Web (par défaut: %s)",
"disable the development webserver": "désactiver le serveur de développement Web",
"simple mode, disables the dashboard": "mode simple, désactive le tableau de bord",
"no colors in case it's needed for compatbility purposes": "pas de couleur au cas où cela serait nécessaire à des fins de compatibilité",
"filename to output logs (default: %s)": "nom de fichier pour les journaux de sortie (par défaut: %s)",
"run dapp (default: %s)": "lancer la dapp (par défaut: %s)",
"Use a specific ethereum client or simulator (supported: %s)": "Utiliser un client ou simulateur ethereum spécifique (supporté:% s)",
"Use a specific ethereum client [%s] (default: %s)": "Utiliser un client ethereum spécifique [%s] (supporté: %s)",
"run blockchain server (default: %s)": "exécuter le serveur blockchain (par défaut: %s)",
"run a fast ethereum rpc simulator": "exécuter un simulateur ethereum RPC rapide",
"use testrpc as the rpc simulator [%s]": "utiliser testrpc comme simulateur RPC [%s]",
@ -76,7 +76,7 @@
"unknown command": "commande inconnue",
"did you mean": "vouliez-vous dire",
"warning: running default config on a non-development environment": "avertissement: exécution de la configuration par défaut sur un environnement de non-développement",
"could not find {{geth_bin}} command; is {{client_name}} installed or in the PATH?": "Impossible de trouver la commande {{geth_bin}}, est-ce que {{client_name}} est installé ou dans le PATH?",
"Ethereum client bin not found:":"Ethereum client bin not found:",
"no accounts found": "Aucun compte trouvé",
"initializing genesis block": "initialisation du bloc de genèse",
"rpcCorsDomain set to *": "rpcCorsDomain défini sur *",

View File

@ -25,7 +25,7 @@
"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)",
"Use a specific ethereum client [%s] (default: %s)": "Usa um cliente de ethereum específico [%s] (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]",
@ -76,7 +76,7 @@
"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?",
"Ethereum client bin not found:":"Ethereum client bin not found:",
"no accounts found": "nenhuma conta encontrada",
"initializing genesis block": "inicializando o bloco de gênese",
"rpcCorsDomain set to *": "rpcCorsDomain definido como *",

16
npm-shrinkwrap.json generated
View File

@ -15327,6 +15327,22 @@
"write-file-atomic": "^2.0.0"
}
},
"ws": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/ws/-/ws-3.0.0.tgz",
"integrity": "sha1-mN2wAFbIOQy3Ued4h4hJf5kQO2w=",
"requires": {
"safe-buffer": "~5.0.1",
"ultron": "~1.1.0"
},
"dependencies": {
"safe-buffer": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.0.1.tgz",
"integrity": "sha1-0mPKVGls2KMGtcplUekt5XkY++c="
}
}
},
"ws": {
"version": "3.3.3",
"resolved": "https://registry.npmjs.org/ws/-/ws-3.3.3.tgz",

View File

@ -16,10 +16,12 @@ module.exports = {
// default environment, merges with the settings in default
// assumed to be the intended environment by `embark run` and `embark blockchain`
development: {
ethereumClientName: "geth", // Can be geth or parity (default:geth)
//ethereumClientBin: "geth", // path to the client binary. Useful if it is not in the global PATH
networkType: "custom", // Can be: testnet, rinkeby, livenet or custom, in which case, it will use the specified networkId
networkId: "1337", // Network id used when networkType is custom
networkId: 1337, // Network id used when networkType is custom
isDev: true, // Uses and ephemeral proof-of-authority network with a pre-funded developer account, mining enabled
datadir: ".embark/development/datadir", // Data directory for the databases and keystore
datadir: ".embark/development/datadir", // Data directory for the databases and keystore (Geth 1.8.15 and Parity 2.0.4 can use the same base folder, till now they does not conflict with each other)
mineWhenNeeded: true, // Uses our custom script (if isDev is false) to mine only when needed
nodiscover: true, // Disables the peer discovery mechanism (manual peer addition)
maxpeers: 0, // Maximum number of network peers (network disabled if set to 0) (default: 25)
@ -30,7 +32,8 @@ module.exports = {
account: {
// numAccounts: 3, // When specified, creates accounts for use in the dapp. This option only works in the development environment, and can be used as a quick start option that bypasses the need for MetaMask in development. These accounts are unlocked and funded with the below settings.
// password: "config/development/password", // Password for the created accounts (as specified in the `numAccounts` setting). If `mineWhenNeeded` is enabled (and isDev is not), this password is used to create a development account controlled by the node.
// balance: "5 ether" // Balance to be given to the created accounts (as specified in the `numAccounts` setting)
// balance: "5 ether", // Balance to be given to the created accounts (as specified in the `numAccounts` setting)
devPassword: "config/development/devpassword" // [Parity-only] File with a void line to unlock the Parity dev account
}
},
@ -38,7 +41,7 @@ module.exports = {
// used with "embark run privatenet" and/or "embark blockchain privatenet"
privatenet: {
networkType: "custom",
networkId: "1337",
networkId: 1337,
isDev: false,
datadir: ".embark/privatenet/datadir",
// -- mineWhenNeeded --
@ -60,8 +63,28 @@ module.exports = {
maxpeers: 0,
proxy: true,
account: {
// "address": "", // When specified, uses that address instead of the default one for the network
password: "config/privatenet/password" // Password to unlock the account. If `mineWhenNeeded` is enabled (and isDev is not), this password is used to create a development account controlled by the node.
// address: "", // When specified, uses that address instead of the default one for the network
password: "config/privatenet/password" // Password to unlock the account
},
targetGasLimit: 8000000,
simulatorMnemonic: "example exile argue silk regular smile grass bomb merge arm assist farm",
simulatorBlocktime: 0
},
privateparitynet: {
ethereumClientName: "parity",
networkType: "custom",
networkId: 1337,
isDev: false,
genesisBlock: "config/privatenet/genesis-parity.json", // Genesis block to initiate on first creation of a development node
datadir: ".embark/privatenet/datadir",
mineWhenNeeded: false,
nodiscover: true,
maxpeers: 0,
proxy: true,
account: {
// address: "", // When specified, uses that address instead of the default one for the network
password: "config/privatenet/password" // Password to unlock the account
},
targetGasLimit: 8000000,
simulatorMnemonic: "example exile argue silk regular smile grass bomb merge arm assist farm",

View File

@ -0,0 +1 @@

View File

@ -0,0 +1 @@
dev_password

File diff suppressed because one or more lines are too long

View File

@ -16,10 +16,12 @@ module.exports = {
// default environment, merges with the settings in default
// assumed to be the intended environment by `embark run` and `embark blockchain`
development: {
ethereumClientName: "geth", // Can be geth or parity (default:geth)
//ethereumClientBin: "geth", // path to the client binary. Useful if it is not in the global PATH
networkType: "custom", // Can be: testnet, rinkeby, livenet or custom, in which case, it will use the specified networkId
networkId: "1337", // Network id used when networkType is custom
networkId: 1337, // Network id used when networkType is custom
isDev: true, // Uses and ephemeral proof-of-authority network with a pre-funded developer account, mining enabled
datadir: ".embark/development/datadir", // Data directory for the databases and keystore
datadir: ".embark/development/datadir", // Data directory for the databases and keystore (Geth 1.8.15 and Parity 2.0.4 can use the same base folder, till now they does not conflict with each other)
mineWhenNeeded: true, // Uses our custom script (if isDev is false) to mine only when needed
nodiscover: true, // Disables the peer discovery mechanism (manual peer addition)
maxpeers: 0, // Maximum number of network peers (network disabled if set to 0) (default: 25)
@ -30,7 +32,8 @@ module.exports = {
account: {
// numAccounts: 3, // When specified, creates accounts for use in the dapp. This option only works in the development environment, and can be used as a quick start option that bypasses the need for MetaMask in development. These accounts are unlocked and funded with the below settings.
// password: "config/development/password", // Password for the created accounts (as specified in the `numAccounts` setting). If `mineWhenNeeded` is enabled (and isDev is not), this password is used to create a development account controlled by the node.
// balance: "5 ether" // Balance to be given to the created accounts (as specified in the `numAccounts` setting)
// balance: "5 ether", // Balance to be given to the created accounts (as specified in the `numAccounts` setting)
devPassword: "config/development/devpassword" // [Parity-only] File with a void line to unlock the Parity dev account
}
},
@ -38,7 +41,7 @@ module.exports = {
// used with "embark run privatenet" and/or "embark blockchain privatenet"
privatenet: {
networkType: "custom",
networkId: "1337",
networkId: 1337,
isDev: false,
datadir: ".embark/privatenet/datadir",
// -- mineWhenNeeded --
@ -70,6 +73,28 @@ module.exports = {
simulatorBlocktime: 0
},
// merges with the settings in default
// used with "embark run privatparityenet" and/or "embark blockchain privateparitynet"
privateparitynet: {
ethereumClientName: "parity",
networkType: "custom",
networkId: 1337,
isDev: false,
genesisBlock: "config/privatenet/genesis-parity.json", // Genesis block to initiate on first creation of a development node
datadir: ".embark/privatenet/datadir", // (Geth 1.8.15 and Parity 2.0.4 can use the same base folder, till now they does not conflict with each other)
mineWhenNeeded: false,
nodiscover: true,
maxpeers: 0,
proxy: true,
account: {
// address: "", // When specified, uses that address instead of the default one for the network
password: "config/privatenet/password" // Password to unlock the account
},
targetGasLimit: 8000000,
simulatorMnemonic: "example exile argue silk regular smile grass bomb merge arm assist farm",
simulatorBlocktime: 0
},
// merges with the settings in default
// used with "embark run testnet" and/or "embark blockchain testnet"
testnet: {
@ -90,7 +115,7 @@ module.exports = {
account: {
password: "config/livenet/password"
}
},
}
// you can name an environment with specific settings and then specify with
// "embark run custom_name" or "embark blockchain custom_name"

View File

@ -0,0 +1 @@

View File

@ -0,0 +1 @@
dev_password

File diff suppressed because one or more lines are too long

View File

@ -1,30 +1,29 @@
/*globals describe, it*/
const Blockchain = require('../lib/modules/blockchain_process/blockchain.js');
const Blockchain = require('../lib/modules/blockchain_process/blockchain.js');
const constants = require('../lib/constants.json');
const {defaultHost} = require('../lib/utils/host');
const assert = require('assert');
describe('embark.Blockchain', function () {
//let Client = function() {};
//Client.prototype.name = "ClientName";
describe('embark.Blockchain', function() {
describe('#initializer', function () {
//let client = new Client();
describe('#initializer', function() {
describe('with empty config', function () {
it('should have a default config', function (done) {
let config = {
describe('with empty config', function() {
it('should have a default config', function(done) {
let blockchain = new Blockchain({});
let expectedConfig = {
networkType: 'custom',
genesisBlock: false,
geth_bin: 'geth',
ethereumClientName: 'geth',
ethereumClientBin: 'geth',
datadir: false,
mineWhenNeeded: false,
rpcHost: defaultHost,
rpcPort: 8545,
rpcApi: ['eth', 'web3', 'net', 'debug'],
rpcCorsDomain: false,
networkId: 12301,
networkId: 1337,
port: 30303,
nodiscover: false,
maxpeers: 25,
@ -32,36 +31,35 @@ describe('embark.Blockchain', function () {
vmdebug: false,
whisper: true,
account: {},
devPassword: "",
bootnodes: "",
wsApi: ["eth", "web3", "net", "shh", "debug"],
wsApi: ["eth", "web3", "net", "shh", "debug", "pubsub"],
wsHost: defaultHost,
wsOrigins: false,
wsPort: 8546,
wsRPC: true,
targetGasLimit: false,
syncmode: undefined,
syncMode: undefined,
syncmode: undefined,
verbosity: undefined,
proxy: true
proxy: true,
silent: undefined
};
let blockchain = new Blockchain(config, 'geth');
// We check also proxy's ports because proxy is set to true
expectedConfig.wsPort += constants.blockchain.servicePortOnProxy;
expectedConfig.rpcPort += constants.blockchain.servicePortOnProxy;
if(config.proxy){
config.wsPort += constants.blockchain.servicePortOnProxy;
config.rpcPort += constants.blockchain.servicePortOnProxy;
}
assert.deepEqual(blockchain.config, config);
assert.deepEqual(blockchain.config, expectedConfig);
done();
});
});
describe('with config', function () {
it('should take config params', function (done) {
describe('with config', function() {
it('should take config params', function(done) {
let config = {
networkType: 'livenet',
genesisBlock: 'foo/bar/genesis.json',
geth_bin: 'geth',
ethereumClientName: 'parity',
ethereumClientBin: 'parity',
datadir: '/foo/datadir/',
mineWhenNeeded: true,
rpcHost: defaultHost,
@ -76,6 +74,7 @@ describe('embark.Blockchain', function () {
vmdebug: false,
whisper: false,
account: {},
devPassword: "foo/bar/devpassword",
bootnodes: "",
wsApi: ["eth", "web3", "net", "shh", "debug"],
wsHost: defaultHost,
@ -84,18 +83,48 @@ describe('embark.Blockchain', function () {
wsRPC: true,
targetGasLimit: false,
syncMode: undefined,
syncmode: undefined,
verbosity: undefined,
proxy: true
};
let blockchain = new Blockchain(config, 'geth');
let blockchain = new Blockchain(config);
if(config.proxy){
config.wsPort += constants.blockchain.servicePortOnProxy;
config.rpcPort += constants.blockchain.servicePortOnProxy;
}
let expectedConfig = {
networkType: 'livenet',
genesisBlock: 'foo/bar/genesis.json',
ethereumClientName: 'parity',
ethereumClientBin: 'parity',
datadir: '/foo/datadir/',
mineWhenNeeded: true,
rpcHost: defaultHost,
rpcPort: 12345,
rpcApi: ['eth', 'web3', 'net', 'debug'],
rpcCorsDomain: true,
networkId: 1,
port: 123456,
nodiscover: true,
maxpeers: 25,
mine: true,
vmdebug: false,
whisper: false,
account: {},
devPassword: "foo/bar/devpassword",
bootnodes: "",
wsApi: ["eth", "web3", "net", "shh", "debug"],
wsHost: defaultHost,
wsOrigins: false,
wsPort: 12346,
wsRPC: true,
targetGasLimit: false,
syncMode: undefined,
verbosity: undefined,
proxy: true,
silent: undefined
};
// We check also proxy's ports because proxy is set to true
expectedConfig.wsPort += constants.blockchain.servicePortOnProxy;
expectedConfig.rpcPort += constants.blockchain.servicePortOnProxy;
assert.deepEqual(blockchain.config, config);
assert.deepEqual(blockchain.config, expectedConfig);
done();
});
});

View File

@ -10,7 +10,7 @@ const FakeIpcProvider = require('./helpers/fakeIpcProvider');
const utils = require('../lib/utils/utils');
i18n.setOrDetectLocale('en');
describe('embark.DevFunds', function () {
describe('embark.DevFunds', function() {
let config = {
networkType: 'livenet',
genesisBlock: 'foo/bar/genesis.json',
@ -41,7 +41,6 @@ describe('embark.DevFunds', function () {
wsRPC: true,
targetGasLimit: false,
syncMode: undefined,
syncmode: undefined,
verbosity: undefined,
proxy: true
};
@ -51,7 +50,7 @@ describe('embark.DevFunds', function () {
config.rpcPort += constants.blockchain.servicePortOnProxy;
}
describe('#create, fund, and unlock accounts', function () {
describe('#create, fund, and unlock accounts', function() {
let provider = new FakeIpcProvider();
const web3 = new Web3(provider);
let devFunds;
@ -61,7 +60,8 @@ describe('embark.DevFunds', function () {
devFunds = await DevFunds.new({blockchainConfig: config, provider: provider, logger: new TestLogger({})});
});
it('should create correct number of accounts', function (done) {
// TOCHECK: DevFunds does not provide this function anymore, please consider to remove this test
it('should create correct number of accounts', function(done) {
provider.injectResult('0x11f4d0a3c12e86b4b5f39b213f7e19d048276dae'); // createAccount #1
provider.injectResult('0x22f4d0a3c12e86b4b5f39b213f7e19d048276dab'); // createAccount #2
@ -81,7 +81,8 @@ describe('embark.DevFunds', function () {
});
});
it('should unlock accounts', function (done) {
// TOCHECK: DevFunds does not provide this function anymore, please consider to remove this test
it('should unlock accounts', function(done) {
if (devFunds.accounts.length === 0) {
assert.equal(true, true, "no accounts to unlock");
return done();
@ -97,7 +98,7 @@ describe('embark.DevFunds', function () {
});
});
it('should fund accounts', function (done) {
it('should fund accounts', function(done) {
if (devFunds.accounts.length === 0) {
assert.equal(true, true, "no accounts to fund");
@ -108,7 +109,7 @@ describe('embark.DevFunds', function () {
// provider.injectResult('0x11f4d0A3c12e86B4b5F39B213F7E19D048276DAe'); // send tx response
});
devFunds.fundAccounts(devFunds.balance, (errFundAccounts) => {
devFunds.fundAccounts(false, (errFundAccounts) => {
assert.equal(errFundAccounts, null);
@ -136,7 +137,7 @@ describe('embark.DevFunds', function () {
cb();
}).catch(cb);
}, function (errAcctsBalance) {
}, function(errAcctsBalance) {
if (errAcctsBalance) throw errAcctsBalance;
done();
});

View File

@ -19,9 +19,9 @@
"integrity": "sha512-5PgPDV6F5s69XNznTcP0za3qH7qgBkr9DVQTXfZtpF+3iEyuIZB1Mjxu52F5CFxgzQUQJoBYHVxtH4Itdb5MgA==",
"dev": true,
"requires": {
"chalk": "2.4.1",
"esutils": "2.0.2",
"js-tokens": "3.0.2"
"chalk": "^2.0.0",
"esutils": "^2.0.2",
"js-tokens": "^3.0.0"
},
"dependencies": {
"js-tokens": {
@ -38,7 +38,7 @@
"integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
"dev": true,
"requires": {
"color-convert": "1.9.2"
"color-convert": "^1.9.0"
}
},
"asap": {
@ -51,8 +51,8 @@
"resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz",
"integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=",
"requires": {
"core-js": "2.5.7",
"regenerator-runtime": "0.11.1"
"core-js": "^2.4.0",
"regenerator-runtime": "^0.11.0"
},
"dependencies": {
"core-js": {
@ -73,9 +73,9 @@
"integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==",
"dev": true,
"requires": {
"ansi-styles": "3.2.1",
"escape-string-regexp": "1.0.5",
"supports-color": "5.4.0"
"ansi-styles": "^3.2.1",
"escape-string-regexp": "^1.0.5",
"supports-color": "^5.3.0"
}
},
"classnames": {
@ -111,7 +111,7 @@
"embark-service": {
"version": "file:extensions/embark-service",
"requires": {
"haml": "0.4.3"
"haml": "^0.4.3"
}
},
"encoding": {
@ -119,7 +119,7 @@
"resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.12.tgz",
"integrity": "sha1-U4tm8+5izRq1HsMjgp0flIDHS+s=",
"requires": {
"iconv-lite": "0.4.23"
"iconv-lite": "~0.4.13"
}
},
"escape-string-regexp": {
@ -139,13 +139,13 @@
"resolved": "https://registry.npmjs.org/fbjs/-/fbjs-0.8.17.tgz",
"integrity": "sha1-xNWY6taUkRJlPWWIsBpc3Nn5D90=",
"requires": {
"core-js": "1.2.7",
"isomorphic-fetch": "2.2.1",
"loose-envify": "1.4.0",
"object-assign": "4.1.1",
"promise": "7.3.1",
"setimmediate": "1.0.5",
"ua-parser-js": "0.7.18"
"core-js": "^1.0.0",
"isomorphic-fetch": "^2.1.1",
"loose-envify": "^1.0.0",
"object-assign": "^4.1.0",
"promise": "^7.1.1",
"setimmediate": "^1.0.5",
"ua-parser-js": "^0.7.18"
}
},
"haml": {
@ -164,7 +164,7 @@
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.23.tgz",
"integrity": "sha512-neyTUVFtahjf0mB3dZT77u+8O0QB89jFdnBkd5P1JgYPbPaia3gXXOVL2fq8VyU2gMMD7SaN7QukTB/pmXYvDA==",
"requires": {
"safer-buffer": "2.1.2"
"safer-buffer": ">= 2.1.2 < 3"
}
},
"invariant": {
@ -172,7 +172,7 @@
"resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz",
"integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==",
"requires": {
"loose-envify": "1.4.0"
"loose-envify": "^1.0.0"
}
},
"is-stream": {
@ -185,8 +185,8 @@
"resolved": "https://registry.npmjs.org/isomorphic-fetch/-/isomorphic-fetch-2.2.1.tgz",
"integrity": "sha1-YRrhrPFPXoH3KVB0coGf6XM1WKk=",
"requires": {
"node-fetch": "1.7.3",
"whatwg-fetch": "2.0.4"
"node-fetch": "^1.0.1",
"whatwg-fetch": ">=0.10.0"
}
},
"jquery": {
@ -209,7 +209,7 @@
"resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
"integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
"requires": {
"js-tokens": "4.0.0"
"js-tokens": "^3.0.0 || ^4.0.0"
}
},
"node-fetch": {
@ -217,8 +217,8 @@
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-1.7.3.tgz",
"integrity": "sha512-NhZ4CsKx7cYm2vSrBAr2PvFOe6sWDf0UYLRqA6svUYg7+/TSfVAu49jYC4BvQ4Sms9SZgdqGBgroqfDhJdTyKQ==",
"requires": {
"encoding": "0.1.12",
"is-stream": "1.1.0"
"encoding": "^0.1.11",
"is-stream": "^1.0.1"
}
},
"object-assign": {
@ -231,7 +231,7 @@
"resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz",
"integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==",
"requires": {
"asap": "2.0.6"
"asap": "~2.0.3"
}
},
"prop-types": {
@ -239,8 +239,8 @@
"resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.6.2.tgz",
"integrity": "sha512-3pboPvLiWD7dkI3qf3KbUe6hKFKa52w+AE0VCqECtf+QHAKgOL37tTaNCnuX1nAAQ4ZhyP+kYVKf8rLmJ/feDQ==",
"requires": {
"loose-envify": "1.4.0",
"object-assign": "4.1.1"
"loose-envify": "^1.3.1",
"object-assign": "^4.1.1"
}
},
"prop-types-extra": {
@ -248,8 +248,8 @@
"resolved": "https://registry.npmjs.org/prop-types-extra/-/prop-types-extra-1.1.0.tgz",
"integrity": "sha512-QFyuDxvMipmIVKD2TwxLVPzMnO4e5oOf1vr3tJIomL8E7d0lr6phTHd5nkPhFIzTD1idBLLEPeylL9g+rrTzRg==",
"requires": {
"react-is": "16.4.2",
"warning": "3.0.0"
"react-is": "^16.3.2",
"warning": "^3.0.0"
}
},
"react": {
@ -257,10 +257,10 @@
"resolved": "https://registry.npmjs.org/react/-/react-16.4.2.tgz",
"integrity": "sha512-dMv7YrbxO4y2aqnvA7f/ik9ibeLSHQJTI6TrYAenPSaQ6OXfb+Oti+oJiy8WBxgRzlKatYqtCjphTgDSCEiWFg==",
"requires": {
"fbjs": "0.8.17",
"loose-envify": "1.4.0",
"object-assign": "4.1.1",
"prop-types": "15.6.2"
"fbjs": "^0.8.16",
"loose-envify": "^1.1.0",
"object-assign": "^4.1.1",
"prop-types": "^15.6.0"
}
},
"react-bootstrap": {
@ -268,18 +268,18 @@
"resolved": "https://registry.npmjs.org/react-bootstrap/-/react-bootstrap-0.32.1.tgz",
"integrity": "sha512-RbfzKUbsukWsToWqGHfCCyMFq9QQI0TznutdyxyJw6dih2NvIne25Mrssg8LZsprqtPpyQi8bN0L0Fx3fUsL8Q==",
"requires": {
"babel-runtime": "6.26.0",
"classnames": "2.2.6",
"dom-helpers": "3.3.1",
"invariant": "2.2.4",
"keycode": "2.2.0",
"prop-types": "15.6.2",
"prop-types-extra": "1.1.0",
"react-overlays": "0.8.3",
"react-prop-types": "0.4.0",
"react-transition-group": "2.4.0",
"uncontrollable": "4.1.0",
"warning": "3.0.0"
"babel-runtime": "^6.11.6",
"classnames": "^2.2.5",
"dom-helpers": "^3.2.0",
"invariant": "^2.2.1",
"keycode": "^2.1.2",
"prop-types": "^15.5.10",
"prop-types-extra": "^1.0.1",
"react-overlays": "^0.8.0",
"react-prop-types": "^0.4.0",
"react-transition-group": "^2.0.0",
"uncontrollable": "^4.1.0",
"warning": "^3.0.0"
}
},
"react-dom": {
@ -287,10 +287,10 @@
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-16.4.2.tgz",
"integrity": "sha512-Usl73nQqzvmJN+89r97zmeUpQDKDlh58eX6Hbs/ERdDHzeBzWy+ENk7fsGQ+5KxArV1iOFPT46/VneklK9zoWw==",
"requires": {
"fbjs": "0.8.17",
"loose-envify": "1.4.0",
"object-assign": "4.1.1",
"prop-types": "15.6.2"
"fbjs": "^0.8.16",
"loose-envify": "^1.1.0",
"object-assign": "^4.1.1",
"prop-types": "^15.6.0"
}
},
"react-is": {
@ -308,12 +308,12 @@
"resolved": "https://registry.npmjs.org/react-overlays/-/react-overlays-0.8.3.tgz",
"integrity": "sha512-h6GT3jgy90PgctleP39Yu3eK1v9vaJAW73GOA/UbN9dJ7aAN4BTZD6793eI1D5U+ukMk17qiqN/wl3diK1Z5LA==",
"requires": {
"classnames": "2.2.6",
"dom-helpers": "3.3.1",
"prop-types": "15.6.2",
"prop-types-extra": "1.1.0",
"react-transition-group": "2.4.0",
"warning": "3.0.0"
"classnames": "^2.2.5",
"dom-helpers": "^3.2.1",
"prop-types": "^15.5.10",
"prop-types-extra": "^1.0.1",
"react-transition-group": "^2.2.0",
"warning": "^3.0.0"
}
},
"react-prop-types": {
@ -321,7 +321,7 @@
"resolved": "https://registry.npmjs.org/react-prop-types/-/react-prop-types-0.4.0.tgz",
"integrity": "sha1-+ZsL+0AGkpya8gUefBQUpcdbk9A=",
"requires": {
"warning": "3.0.0"
"warning": "^3.0.0"
}
},
"react-transition-group": {
@ -329,10 +329,10 @@
"resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-2.4.0.tgz",
"integrity": "sha512-Xv5d55NkJUxUzLCImGSanK8Cl/30sgpOEMGc5m86t8+kZwrPxPCPcFqyx83kkr+5Lz5gs6djuvE5By+gce+VjA==",
"requires": {
"dom-helpers": "3.3.1",
"loose-envify": "1.4.0",
"prop-types": "15.6.2",
"react-lifecycles-compat": "3.0.4"
"dom-helpers": "^3.3.1",
"loose-envify": "^1.3.1",
"prop-types": "^15.6.2",
"react-lifecycles-compat": "^3.0.4"
}
},
"regenerator-runtime": {
@ -356,7 +356,7 @@
"integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==",
"dev": true,
"requires": {
"has-flag": "3.0.0"
"has-flag": "^3.0.0"
}
},
"ua-parser-js": {
@ -369,7 +369,7 @@
"resolved": "https://registry.npmjs.org/uncontrollable/-/uncontrollable-4.1.0.tgz",
"integrity": "sha1-4DWCkSUuGGUiLZCTmxny9J+Bwak=",
"requires": {
"invariant": "2.2.4"
"invariant": "^2.1.0"
}
},
"warning": {
@ -377,7 +377,7 @@
"resolved": "https://registry.npmjs.org/warning/-/warning-3.0.0.tgz",
"integrity": "sha1-MuU3fLVy3kqwR1O9+IIcAe1gW3w=",
"requires": {
"loose-envify": "1.4.0"
"loose-envify": "^1.0.0"
}
},
"whatwg-fetch": {