mirror of https://github.com/embarklabs/embark.git
feat(@embark/nethermind): add Nethermind blockchain client plugin
This commit is contained in:
parent
4190d5ec65
commit
6db8d8750a
|
@ -77,6 +77,8 @@ export class Config {
|
|||
|
||||
version: string;
|
||||
|
||||
locale: string;
|
||||
|
||||
shownNoAccountConfigMsg = false; // flag to ensure "no account config" message is only displayed once to the user
|
||||
|
||||
corsParts: string[] = [];
|
||||
|
@ -95,6 +97,7 @@ export class Config {
|
|||
this.configDir = options.configDir || DEFAULT_CONFIG_PATH;
|
||||
this.chainsFile = options.chainsFile;
|
||||
this.plugins = options.plugins;
|
||||
this.locale = options.locale || 'en';
|
||||
this.logger = options.logger;
|
||||
this.package = options.package;
|
||||
this.events = options.events;
|
||||
|
|
|
@ -138,10 +138,6 @@ export class Plugin {
|
|||
this._loggerObject[type](this.name + ':', ...[].slice.call(arguments, 1));
|
||||
}
|
||||
|
||||
setUpLogger() {
|
||||
this.logger = new Logger({});
|
||||
}
|
||||
|
||||
isContextValid() {
|
||||
if (this.currentContext.includes(constants.contexts.any) || this.acceptedContext.includes(constants.contexts.any)) {
|
||||
return true;
|
||||
|
@ -161,9 +157,6 @@ export class Plugin {
|
|||
return false;
|
||||
}
|
||||
this.loaded = true;
|
||||
if (this.shouldInterceptLogs) {
|
||||
this.setUpLogger();
|
||||
}
|
||||
if (isEs6Module(this.pluginModule)) {
|
||||
if (this.pluginModule.default) {
|
||||
this.pluginModule = this.pluginModule.default;
|
||||
|
|
|
@ -85,7 +85,16 @@ export class Engine {
|
|||
const options = _options || {};
|
||||
this.events = options.events || this.events || new Events();
|
||||
this.logger = this.logger || new Logger({context: this.context, logLevel: options.logLevel || this.logLevel || 'info', events: this.events, logFile: this.logFile});
|
||||
this.config = new Config({env: this.env, logger: this.logger, events: this.events, context: this.context, webServerConfig: this.webServerConfig, version: this.version, package: this.package});
|
||||
this.config = new Config({
|
||||
env: this.env,
|
||||
logger: this.logger,
|
||||
events: this.events,
|
||||
context: this.context,
|
||||
webServerConfig: this.webServerConfig,
|
||||
version: this.version,
|
||||
package: this.package,
|
||||
locale: this.locale
|
||||
});
|
||||
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);
|
||||
|
|
|
@ -0,0 +1,62 @@
|
|||
const WebSocket = require("ws");
|
||||
const http = require("http");
|
||||
const https = require("https");
|
||||
|
||||
const LIVENESS_CHECK=`{"jsonrpc":"2.0","method":"web3_clientVersion","params":[],"id":42}`;
|
||||
|
||||
const parseAndRespond = (data, cb) => {
|
||||
let resp;
|
||||
try {
|
||||
resp = JSON.parse(data);
|
||||
if (resp.error) {
|
||||
return cb(resp.error);
|
||||
}
|
||||
} catch (e) {
|
||||
return cb('Version data is not valid JSON');
|
||||
}
|
||||
if (!resp || !resp.result) {
|
||||
return cb('No version returned');
|
||||
}
|
||||
const [_, version, __] = resp.result.split('/');
|
||||
cb(null, version);
|
||||
};
|
||||
|
||||
const testRpcWithEndpoint = (endpoint, cb) => {
|
||||
const options = {
|
||||
method: "POST",
|
||||
timeout: 1000,
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
"Content-Length": Buffer.byteLength(LIVENESS_CHECK)
|
||||
}
|
||||
};
|
||||
|
||||
let obj = http;
|
||||
if (endpoint.startsWith('https')) {
|
||||
obj = https;
|
||||
}
|
||||
|
||||
const req = obj.request(endpoint, options, (res) => {
|
||||
let data = "";
|
||||
res.on("data", chunk => { data += chunk; });
|
||||
res.on("end", () => parseAndRespond(data, cb));
|
||||
});
|
||||
req.on("error", (e) => cb(e));
|
||||
req.write(LIVENESS_CHECK);
|
||||
req.end();
|
||||
};
|
||||
|
||||
const testWsEndpoint = (endpoint, cb) => {
|
||||
const conn = new WebSocket(endpoint);
|
||||
conn.on("message", (data) => {
|
||||
parseAndRespond(data, cb);
|
||||
conn.close();
|
||||
});
|
||||
conn.on("open", () => conn.send(LIVENESS_CHECK));
|
||||
conn.on("error", (e) => cb(e));
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
testWsEndpoint,
|
||||
testRpcWithEndpoint
|
||||
};
|
|
@ -6,6 +6,7 @@ const clipboardy = require('clipboardy');
|
|||
import { canonicalHost } from './host';
|
||||
export { canonicalHost, defaultCorsHost, defaultHost, dockerHostSwap, isDocker } from './host';
|
||||
export { downloadFile, findNextPort, getJson, httpGet, httpsGet, httpGetJson, httpsGetJson, pingEndpoint } from './network';
|
||||
export { testRpcWithEndpoint, testWsEndpoint } from './check';
|
||||
const logUtils = require('./log-utils');
|
||||
export const escapeHtml = logUtils.escapeHtml;
|
||||
export const normalizeInput = logUtils.normalizeInput;
|
||||
|
|
|
@ -58,13 +58,22 @@ class EmbarkController {
|
|||
});
|
||||
|
||||
engine.init({}, () => {
|
||||
Object.assign(engine.config.blockchainConfig, { isStandalone: true });
|
||||
|
||||
engine.registerModuleGroup("coreComponents");
|
||||
engine.registerModuleGroup("blockchainStackComponents");
|
||||
engine.registerModuleGroup("blockchain");
|
||||
|
||||
// load custom plugins
|
||||
engine.loadDappPlugins();
|
||||
let pluginList = engine.plugins.listPlugins();
|
||||
if (pluginList.length > 0) {
|
||||
engine.logger.info(__("loaded plugins") + ": " + pluginList.join(", "));
|
||||
}
|
||||
|
||||
engine.startEngine(async () => {
|
||||
try {
|
||||
const alreadyStarted = await engine.events.request2("blockchain:node:start", Object.assign(engine.config.blockchainConfig, { isStandalone: true }));
|
||||
const alreadyStarted = await engine.events.request2("blockchain:node:start", engine.config.blockchainConfig);
|
||||
if (alreadyStarted) {
|
||||
engine.logger.warn(__('Blockchain process already started. No need to run `embark blockchain`'));
|
||||
process.exit(0);
|
||||
|
|
|
@ -167,7 +167,7 @@ describe('embark.Config', function () {
|
|||
},
|
||||
"datadir": ".embark/extNetwork/datadir",
|
||||
"rpcHost": "mynetwork.com",
|
||||
"rpcPort": undefined,
|
||||
"rpcPort": false,
|
||||
"rpcCorsDomain": {
|
||||
"auto": true,
|
||||
"additionalCors": []
|
||||
|
|
|
@ -64,7 +64,7 @@ class EthereumBlockchainClient {
|
|||
const code = contract.code.substring(0, 2) === '0x' ? contract.code : "0x" + contract.code;
|
||||
const contractObject = contractObj.deploy({arguments: (contract.args || []), data: code});
|
||||
if (contract.gas === 'auto' || !contract.gas) {
|
||||
const gasValue = await contractObject.estimateGas();
|
||||
const gasValue = await contractObject.estimateGas({value: 0, from: account});
|
||||
const increase_per = 1 + (Math.random() / 10.0);
|
||||
contract.gas = Math.floor(gasValue * increase_per);
|
||||
}
|
||||
|
|
|
@ -35,7 +35,7 @@ class Blockchain {
|
|||
this.config = {
|
||||
silent: this.userConfig.silent,
|
||||
client: this.userConfig.client,
|
||||
ethereumClientBin: this.userConfig.ethereumClientBin || this.userConfig.client,
|
||||
ethereumClientBin: this.userConfig.ethereumClientBin,
|
||||
networkType: this.userConfig.networkType || clientClass.DEFAULTS.NETWORK_TYPE,
|
||||
networkId: this.userConfig.networkId || clientClass.DEFAULTS.NETWORK_ID,
|
||||
genesisBlock: this.userConfig.genesisBlock || false,
|
||||
|
|
|
@ -12,6 +12,7 @@ class Geth {
|
|||
this.embarkConfig = embark.config.embarkConfig;
|
||||
this.blockchainConfig = embark.config.blockchainConfig;
|
||||
this.communicationConfig = embark.config.communicationConfig;
|
||||
// TODO get options from config instead of options
|
||||
this.locale = options.locale;
|
||||
this.logger = embark.logger;
|
||||
this.client = options.client;
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
engine-strict = true
|
||||
package-lock = false
|
||||
save-exact = true
|
||||
scripts-prepend-node-path = true
|
|
@ -0,0 +1,20 @@
|
|||
# `embark-nethermind`
|
||||
|
||||
> Nethermind blockchain client plugin for Embark
|
||||
|
||||
|
||||
## Quick docs
|
||||
|
||||
To configure the Netherminds client, you can use the Embark configs as always, or for more control, use the Nethermind config files.
|
||||
To change them, go in your Netherminds directory, then in `configs/`. There, you will see all the configuration files for the different networks.
|
||||
If you ever need to run a different network than dev, testnet or mainnet, you can change it in the Embark blockchain configuration by changing the `networkType` to the name of the config file, without the `.cfg`.
|
||||
Eg: For the Goerli network, just put `networkType: 'goerli`
|
||||
Note: The dev mode of Netherminds is called `ndm` and the config file is `ndm_consumer_local.cfg`. Using `miningMode: 'dev'` automatically translates to using that config file.
|
||||
|
||||
## Websocket support
|
||||
|
||||
Even though Nethermind supports Websocket connections, it does not support `eth_subscribe`, so you will not be able to use contract events.
|
||||
Also, please note that you will need to change the `endpoint` in the blockchain configuration to `ws://localhost:8545/ws/json-rpc` when working in local. Do change the port or the host to whatever you need.
|
||||
|
||||
Visit [embark.status.im](https://embark.status.im/) to get started with
|
||||
[Embark](https://github.com/embark-framework/embark).
|
|
@ -0,0 +1,67 @@
|
|||
{
|
||||
"name": "embark-nethermind",
|
||||
"version": "5.0.0-alpha.9",
|
||||
"author": "Iuri Matias <iuri.matias@gmail.com>",
|
||||
"contributors": [],
|
||||
"description": "Nethermind blockchain client plugin for Embark",
|
||||
"homepage": "https://github.com/embark-framework/embark/tree/master/packages/plugins/nethermind#readme",
|
||||
"bugs": "https://github.com/embark-framework/embark/issues",
|
||||
"keywords": [
|
||||
"blockchain",
|
||||
"dapps",
|
||||
"ethereum",
|
||||
"serverless",
|
||||
"nethermind"
|
||||
],
|
||||
"files": [
|
||||
"dist"
|
||||
],
|
||||
"license": "MIT",
|
||||
"repository": {
|
||||
"directory": "packages/plugins/nethermind",
|
||||
"type": "git",
|
||||
"url": "https://github.com/embark-framework/embark.git"
|
||||
},
|
||||
"main": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts",
|
||||
"embark-collective": {
|
||||
"build:node": true,
|
||||
"typecheck": true
|
||||
},
|
||||
"scripts": {
|
||||
"_build": "npm run solo -- build",
|
||||
"_typecheck": "npm run solo -- typecheck",
|
||||
"ci": "npm run qa",
|
||||
"clean": "npm run reset",
|
||||
"lint": "eslint src/",
|
||||
"qa": "npm-run-all lint _typecheck _build",
|
||||
"reset": "npx rimraf dist embark-*.tgz package",
|
||||
"solo": "embark-solo"
|
||||
},
|
||||
"eslintConfig": {
|
||||
"extends": "../../../.eslintrc.json"
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/runtime-corejs3": "7.7.4",
|
||||
"async": "2.6.1",
|
||||
"core-js": "3.4.3",
|
||||
"embark-core": "^5.0.0-alpha.9",
|
||||
"embark-i18n": "^5.0.0-alpha.5",
|
||||
"embark-utils": "^5.0.0-alpha.9",
|
||||
"fs-extra": "8.1.0",
|
||||
"netcat": "1.3.5",
|
||||
"semver": "5.6.0",
|
||||
"ws": "7.1.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"embark-solo": "^5.0.0-alpha.5",
|
||||
"eslint": "5.7.0",
|
||||
"npm-run-all": "4.1.5",
|
||||
"rimraf": "3.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10.17.0 <12.0.0",
|
||||
"npm": ">=6.11.3",
|
||||
"yarn": ">=1.19.1"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,301 @@
|
|||
import {__} from 'embark-i18n';
|
||||
const async = require('async');
|
||||
const {spawn, exec} = require('child_process');
|
||||
const path = require('path');
|
||||
const constants = require('embark-core/constants');
|
||||
const NethermindClient = require('./nethermindClient.js');
|
||||
import {IPC} from 'embark-core';
|
||||
|
||||
import {compact, dappPath, defaultHost, dockerHostSwap, embarkPath} from 'embark-utils';
|
||||
import { Logger } from 'embark-logger';
|
||||
|
||||
// time between IPC connection attempts (in ms)
|
||||
const IPC_CONNECT_INTERVAL = 2000;
|
||||
|
||||
class Blockchain {
|
||||
/*eslint complexity: ["error", 50]*/
|
||||
constructor(userConfig, clientClass) {
|
||||
this.userConfig = userConfig;
|
||||
this.env = userConfig.env || 'development';
|
||||
this.isDev = userConfig.isDev;
|
||||
this.onReadyCallback = userConfig.onReadyCallback || (() => {});
|
||||
this.onExitCallback = userConfig.onExitCallback;
|
||||
this.logger = userConfig.logger || new Logger({logLevel: 'debug', context: constants.contexts.blockchain}); // do not pass in events as we don't want any log events emitted
|
||||
this.events = userConfig.events;
|
||||
this.isStandalone = userConfig.isStandalone;
|
||||
this.certOptions = userConfig.certOptions;
|
||||
|
||||
let defaultWsApi = clientClass.DEFAULTS.WS_API;
|
||||
if (this.isDev) defaultWsApi = clientClass.DEFAULTS.DEV_WS_API;
|
||||
|
||||
this.config = {
|
||||
silent: this.userConfig.silent,
|
||||
client: this.userConfig.client,
|
||||
ethereumClientBin: this.userConfig.ethereumClientBin,
|
||||
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,
|
||||
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: {},
|
||||
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,
|
||||
customOptions: this.userConfig.customOptions
|
||||
};
|
||||
|
||||
if (this.userConfig.accounts) {
|
||||
const nodeAccounts = this.userConfig.accounts.find(account => account.nodeAccounts);
|
||||
if (nodeAccounts) {
|
||||
this.config.account = {
|
||||
numAccounts: nodeAccounts.numAddresses || 1,
|
||||
password: nodeAccounts.password,
|
||||
balance: nodeAccounts.balance
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// TODO I think we all do this in config.ts now
|
||||
if (this.userConfig.default || JSON.stringify(this.userConfig) === '{"client":"nethermind"}') {
|
||||
if (this.env === 'development') {
|
||||
this.isDev = true;
|
||||
} else {
|
||||
this.config.genesisBlock = embarkPath("templates/boilerplate/config/privatenet/genesis.json");
|
||||
}
|
||||
this.config.datadir = dappPath(".embark/development/datadir");
|
||||
this.config.wsOrigins = this.config.wsOrigins || "http://localhost:8000";
|
||||
this.config.rpcCorsDomain = this.config.rpcCorsDomain || "http://localhost:8000";
|
||||
this.config.targetGasLimit = 8000000;
|
||||
}
|
||||
this.config.account.devPassword = path.join(this.config.datadir, "devPassword");
|
||||
|
||||
const spaceMessage = 'The path for %s in blockchain config contains spaces, please remove them';
|
||||
if (this.config.datadir && this.config.datadir.indexOf(' ') > 0) {
|
||||
this.logger.error(__(spaceMessage, 'datadir'));
|
||||
process.exit(1);
|
||||
}
|
||||
if (this.config.account.password && this.config.account.password.indexOf(' ') > 0) {
|
||||
this.logger.error(__(spaceMessage, 'accounts.password'));
|
||||
process.exit(1);
|
||||
}
|
||||
if (this.config.genesisBlock && this.config.genesisBlock.indexOf(' ') > 0) {
|
||||
this.logger.error(__(spaceMessage, 'genesisBlock'));
|
||||
process.exit(1);
|
||||
}
|
||||
this.client = new clientClass({config: this.config, env: this.env, isDev: this.isDev, logger: this.logger});
|
||||
|
||||
this.initStandaloneProcess();
|
||||
}
|
||||
|
||||
/**
|
||||
* Polls for a connection to an IPC server (generally this is set up
|
||||
* in the Embark process). Once connected, any logs logged to the
|
||||
* Logger will be shipped off to the IPC server. In the case of `embark
|
||||
* run`, the BlockchainListener module is listening for these logs.
|
||||
*
|
||||
* @returns {void}
|
||||
*/
|
||||
initStandaloneProcess() {
|
||||
if (!this.isStandalone) {
|
||||
return;
|
||||
}
|
||||
let logQueue = [];
|
||||
|
||||
// on every log logged in logger (say that 3x fast), send the log
|
||||
// to the IPC serve listening (only if we're connected of course)
|
||||
this.logger.events.on('log', (logLevel, message) => {
|
||||
if (this.ipc.connected) {
|
||||
this.ipc.request('blockchain:log', {logLevel, message});
|
||||
} else {
|
||||
logQueue.push({logLevel, message});
|
||||
}
|
||||
});
|
||||
|
||||
this.ipc = new IPC({ipcRole: 'client'});
|
||||
|
||||
// Wait for an IPC server to start (ie `embark run`) by polling `.connect()`.
|
||||
// Do not kill this interval as the IPC server may restart (ie restart
|
||||
// `embark run` without restarting `embark blockchain`)
|
||||
setInterval(() => {
|
||||
if (!this.ipc.connected) {
|
||||
this.ipc.connect(() => {
|
||||
if (this.ipc.connected) {
|
||||
logQueue.forEach(message => {
|
||||
this.ipc.request('blockchain:log', message);
|
||||
});
|
||||
logQueue = [];
|
||||
this.ipc.client.on('process:blockchain:stop', () => {
|
||||
this.kill();
|
||||
process.exit(0);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}, IPC_CONNECT_INTERVAL);
|
||||
}
|
||||
|
||||
runCommand(cmd, options, callback) {
|
||||
this.logger.info(__("running: %s", cmd.underline).green);
|
||||
if (this.config.silent) {
|
||||
options.silent = true;
|
||||
}
|
||||
return exec(cmd, options, callback);
|
||||
}
|
||||
|
||||
run() {
|
||||
var self = this;
|
||||
this.logger.info("===============================================================================".magenta);
|
||||
this.logger.info("===============================================================================".magenta);
|
||||
this.logger.info(__("Embark Blockchain using %s", self.client.prettyName.underline).magenta);
|
||||
this.logger.info("===============================================================================".magenta);
|
||||
this.logger.info("===============================================================================".magenta);
|
||||
|
||||
let address = '';
|
||||
async.waterfall([
|
||||
function checkInstallation(next) {
|
||||
self.isClientInstalled((err) => {
|
||||
if (err) {
|
||||
return next({message: err});
|
||||
}
|
||||
next();
|
||||
});
|
||||
},
|
||||
function getMainCommand(next) {
|
||||
self.client.mainCommand(address, (cmd, args) => {
|
||||
next(null, cmd, args);
|
||||
}, true);
|
||||
}
|
||||
], function (err, cmd, args) {
|
||||
if (err) {
|
||||
self.logger.error(err.message);
|
||||
return;
|
||||
}
|
||||
args = compact(args);
|
||||
|
||||
let full_cmd = cmd + " " + args.join(' ');
|
||||
self.logger.info(__("running: %s", full_cmd.underline).green);
|
||||
self.child = spawn(cmd, args, {cwd: process.cwd()});
|
||||
|
||||
self.child.on('error', (err) => {
|
||||
err = err.toString();
|
||||
self.logger.error('Blockchain error: ', err);
|
||||
if (self.env === 'development' && err.indexOf('Failed to unlock') > 0) {
|
||||
self.logger.error('\n' + __('Development blockchain has changed to use the --dev option.').yellow);
|
||||
self.logger.error(__('You can reset your workspace to fix the problem with').yellow + ' embark reset'.cyan);
|
||||
self.logger.error(__('Otherwise, you can change your data directory in blockchain.json (datadir)').yellow);
|
||||
}
|
||||
});
|
||||
|
||||
self.child.stderr.on('data', (data) => {
|
||||
self.logger.info(`${self.client.name} error: ${data}`);
|
||||
});
|
||||
|
||||
self.child.stdout.on('data', async (data) => {
|
||||
data = data.toString();
|
||||
if (!self.readyCalled && self.client.isReady(data)) {
|
||||
self.readyCalled = true;
|
||||
self.readyCallback();
|
||||
}
|
||||
self.logger.info(`${self.client.name}: ${data}`);
|
||||
});
|
||||
|
||||
self.child.on('exit', (code) => {
|
||||
let strCode;
|
||||
if (code) {
|
||||
strCode = 'with error code ' + code;
|
||||
} else {
|
||||
strCode = 'with no error code (manually killed?)';
|
||||
}
|
||||
self.logger.error(self.client.name + ' exited ' + strCode);
|
||||
if (self.onExitCallback) {
|
||||
self.onExitCallback();
|
||||
}
|
||||
});
|
||||
|
||||
self.child.on('uncaughtException', (err) => {
|
||||
self.logger.error('Uncaught ' + self.client.name + ' exception', err);
|
||||
if (self.onExitCallback) {
|
||||
self.onExitCallback();
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
readyCallback() {
|
||||
if (this.onReadyCallback) {
|
||||
this.onReadyCallback();
|
||||
}
|
||||
}
|
||||
|
||||
kill() {
|
||||
this.shutdownProxy();
|
||||
if (this.child) {
|
||||
this.child.kill();
|
||||
}
|
||||
}
|
||||
|
||||
isClientInstalled(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(__('Ethereum client bin not found:') + ' ' + this.client.getBinaryPath());
|
||||
}
|
||||
const parsedVersion = this.client.parseVersion(stdout);
|
||||
const supported = this.client.isSupportedVersion(parsedVersion);
|
||||
if (supported === undefined) {
|
||||
this.logger.warn((__('WARNING! Ethereum client version could not be determined or compared with version range') + ' ' + this.client.versSupported + __(', for best results please use a supported version')));
|
||||
} else if (!supported) {
|
||||
this.logger.warn((__('WARNING! Ethereum client version unsupported, for best results please use a version in range') + ' ' + this.client.versSupported));
|
||||
}
|
||||
callback();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export class BlockchainClient extends Blockchain {
|
||||
constructor(userConfig, options) {
|
||||
if (JSON.stringify(userConfig) === '{"enabled":true}' && options.env !== 'development') {
|
||||
options.logger.info("===> " + __("warning: running default config on a non-development environment"));
|
||||
}
|
||||
// if client is not set in preferences, default is parity
|
||||
if (!userConfig.client) userConfig.client = constants.blockchain.clients.parity;
|
||||
// if clientName is set, it overrides preferences
|
||||
if (options.clientName) userConfig.client = options.clientName;
|
||||
// Choose correct client instance based on clientName
|
||||
let clientClass;
|
||||
switch (userConfig.client) {
|
||||
case 'nethermind':
|
||||
clientClass = NethermindClient;
|
||||
break;
|
||||
default:
|
||||
console.error(__('Unknown client "%s". Please use one of the following: %s', userConfig.client, Object.keys(constants.blockchain.clients).join(', ')));
|
||||
process.exit(1);
|
||||
}
|
||||
userConfig.isDev = (userConfig.isDev || userConfig.default);
|
||||
userConfig.env = options.env;
|
||||
userConfig.onReadyCallback = options.onReadyCallback;
|
||||
userConfig.onExitCallback = options.onExitCallback;
|
||||
userConfig.logger = options.logger;
|
||||
userConfig.certOptions = options.certOptions;
|
||||
userConfig.isStandalone = options.isStandalone;
|
||||
|
||||
super(userConfig, clientClass);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,57 @@
|
|||
import * as i18n from 'embark-i18n';
|
||||
import { ProcessWrapper } from 'embark-core';
|
||||
const constants = require('embark-core/constants');
|
||||
import { BlockchainClient } from './blockchain';
|
||||
|
||||
let blockchainProcess;
|
||||
|
||||
class BlockchainProcess extends ProcessWrapper {
|
||||
constructor(options) {
|
||||
super();
|
||||
this.blockchainConfig = options.blockchainConfig;
|
||||
this.client = options.client;
|
||||
this.env = options.env;
|
||||
this.isDev = options.isDev;
|
||||
this.certOptions = options.certOptions;
|
||||
|
||||
i18n.setOrDetectLocale(options.locale);
|
||||
|
||||
this.blockchainConfig.silent = true;
|
||||
this.blockchain = new BlockchainClient(
|
||||
this.blockchainConfig,
|
||||
{
|
||||
clientName: this.client,
|
||||
env: this.env,
|
||||
certOptions: this.certOptions,
|
||||
onReadyCallback: this.blockchainReady.bind(this),
|
||||
onExitCallback: this.blockchainExit.bind(this),
|
||||
logger: console
|
||||
}
|
||||
);
|
||||
|
||||
this.blockchain.run();
|
||||
}
|
||||
|
||||
blockchainReady() {
|
||||
blockchainProcess.send({result: constants.blockchain.blockchainReady});
|
||||
}
|
||||
|
||||
blockchainExit() {
|
||||
// tell our parent process that ethereum client has exited
|
||||
blockchainProcess.send({result: constants.blockchain.blockchainExit});
|
||||
}
|
||||
|
||||
kill() {
|
||||
this.blockchain.kill();
|
||||
}
|
||||
}
|
||||
|
||||
process.on('message', (msg) => {
|
||||
if (msg === 'exit') {
|
||||
return blockchainProcess.kill();
|
||||
}
|
||||
if (msg.action === constants.blockchain.init) {
|
||||
blockchainProcess = new BlockchainProcess(msg.options);
|
||||
return blockchainProcess.send({result: constants.blockchain.initiated});
|
||||
}
|
||||
});
|
|
@ -0,0 +1,84 @@
|
|||
import { __ } from 'embark-i18n';
|
||||
import { ProcessLauncher } from 'embark-core';
|
||||
import { joinPath } from 'embark-utils';
|
||||
const constants = require('embark-core/constants');
|
||||
|
||||
export class BlockchainProcessLauncher {
|
||||
|
||||
constructor (options) {
|
||||
this.events = options.events;
|
||||
this.env = options.env;
|
||||
this.logger = options.logger;
|
||||
this.normalizeInput = options.normalizeInput;
|
||||
this.blockchainConfig = options.blockchainConfig;
|
||||
this.locale = options.locale;
|
||||
this.isDev = options.isDev;
|
||||
this.client = options.client;
|
||||
this.embark = options.embark;
|
||||
}
|
||||
|
||||
processEnded(code) {
|
||||
this.logger.error(__('Blockchain process ended before the end of this process. Try running blockchain in a separate process using `$ embark blockchain`. Code: %s', code));
|
||||
}
|
||||
|
||||
startBlockchainNode(readyCb) {
|
||||
this.logger.info(__('Starting Blockchain node in another process').cyan);
|
||||
|
||||
this.blockchainProcess = new ProcessLauncher({
|
||||
name: 'blockchain',
|
||||
modulePath: joinPath(__dirname, './blockchainProcess.js'),
|
||||
logger: this.logger,
|
||||
events: this.events,
|
||||
silent: this.logger.logLevel !== 'trace',
|
||||
exitCallback: this.processEnded.bind(this),
|
||||
embark: this.embark
|
||||
});
|
||||
this.blockchainProcess.send({
|
||||
action: constants.blockchain.init, options: {
|
||||
blockchainConfig: this.blockchainConfig,
|
||||
client: this.client,
|
||||
env: this.env,
|
||||
isDev: this.isDev,
|
||||
locale: this.locale,
|
||||
certOptions: this.embark.config.webServerConfig.certOptions,
|
||||
events: this.events
|
||||
}
|
||||
});
|
||||
|
||||
this.blockchainProcess.once('result', constants.blockchain.blockchainReady, () => {
|
||||
this.logger.info(__('Blockchain node is ready').cyan);
|
||||
readyCb();
|
||||
});
|
||||
|
||||
this.blockchainProcess.once('result', constants.blockchain.blockchainExit, () => {
|
||||
// tell 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.setCommandHandler('logs:ethereum:enable', () => {
|
||||
this.blockchainProcess.setSilent(false);
|
||||
});
|
||||
|
||||
this.events.setCommandHandler('logs:ethereum:disable', () => {
|
||||
this.blockchainProcess.setSilent(true);
|
||||
});
|
||||
|
||||
this.events.on('exit', () => {
|
||||
this.blockchainProcess.send('exit');
|
||||
});
|
||||
}
|
||||
|
||||
stopBlockchainNode(cb) {
|
||||
if (this.blockchainProcess) {
|
||||
this.events.once(constants.blockchain.blockchainExit, cb);
|
||||
this.blockchainProcess.exitCallback = () => {}; // don't show error message as the process was killed on purpose
|
||||
this.blockchainProcess.send('exit');
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,121 @@
|
|||
import { __ } from 'embark-i18n';
|
||||
import {BlockchainClient} from "./blockchain";
|
||||
const {normalizeInput, testRpcWithEndpoint, testWsEndpoint} = require('embark-utils');
|
||||
import {BlockchainProcessLauncher} from './blockchainProcessLauncher';
|
||||
|
||||
export const NETHERMIND_NAME = 'nethermind';
|
||||
|
||||
class Nethermind {
|
||||
constructor(embark) {
|
||||
this.embark = embark;
|
||||
this.embarkConfig = embark.config.embarkConfig;
|
||||
this.blockchainConfig = embark.config.blockchainConfig;
|
||||
this.locale = embark.config.locale;
|
||||
this.logger = embark.logger;
|
||||
this.client = embark.config.blockchainConfig.client;
|
||||
this.isDev = embark.config.blockchainConfig.isDev;
|
||||
this.events = embark.events;
|
||||
|
||||
if (!this.shouldInit()) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.events.request("blockchain:node:register", NETHERMIND_NAME, {
|
||||
isStartedFn: (isStartedCb) => {
|
||||
this._doCheck((state) => {
|
||||
console.log('Started?', JSON.stringify(state));
|
||||
return isStartedCb(null, state.status === "on");
|
||||
});
|
||||
},
|
||||
launchFn: (readyCb) => {
|
||||
this.events.request('processes:register', 'blockchain', {
|
||||
launchFn: (cb) => {
|
||||
this.startBlockchainNode(cb);
|
||||
},
|
||||
stopFn: (cb) => {
|
||||
this.stopBlockchainNode(cb);
|
||||
}
|
||||
});
|
||||
this.events.request("processes:launch", "blockchain", (err) => {
|
||||
if (err) {
|
||||
this.logger.error(`Error launching blockchain process: ${err.message || err}`);
|
||||
}
|
||||
readyCb();
|
||||
});
|
||||
this.registerServiceCheck();
|
||||
},
|
||||
stopFn: async (cb) => {
|
||||
await this.events.request("processes:stop", "blockchain");
|
||||
cb();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
shouldInit() {
|
||||
return (
|
||||
this.blockchainConfig.client === NETHERMIND_NAME &&
|
||||
this.blockchainConfig.enabled
|
||||
);
|
||||
}
|
||||
|
||||
_getNodeState(err, version, cb) {
|
||||
if (err) return cb({ name: "Ethereum node not found", status: 'off' });
|
||||
|
||||
return cb({ name: `${NETHERMIND_NAME} (Ethereum)`, status: 'on' });
|
||||
}
|
||||
|
||||
_doCheck(cb) {
|
||||
if (this.blockchainConfig.endpoint.startsWith('ws')) {
|
||||
return testWsEndpoint(this.blockchainConfig.endpoint, (err, version) => this._getNodeState(err, version, cb));
|
||||
}
|
||||
testRpcWithEndpoint(this.blockchainConfig.endpoint, (err, version) => this._getNodeState(err, version, cb));
|
||||
}
|
||||
|
||||
registerServiceCheck() {
|
||||
this.events.request("services:register", 'Ethereum', this._doCheck.bind(this), 5000, 'off');
|
||||
}
|
||||
|
||||
startBlockchainNode(callback) {
|
||||
if (this.blockchainConfig.isStandalone) {
|
||||
return new BlockchainClient(this.blockchainConfig, {
|
||||
clientName: NETHERMIND_NAME,
|
||||
env: this.embark.config.env,
|
||||
certOptions: this.embark.config.webServerConfig.certOptions,
|
||||
logger: this.logger,
|
||||
events: this.events,
|
||||
isStandalone: true,
|
||||
fs: this.embark.fs
|
||||
}).run();
|
||||
}
|
||||
|
||||
this.blockchainProcess = new BlockchainProcessLauncher({
|
||||
events: this.events,
|
||||
env: this.embark.config.env,
|
||||
logger: this.logger,
|
||||
normalizeInput,
|
||||
blockchainConfig: this.blockchainConfig,
|
||||
locale: this.locale,
|
||||
client: this.client,
|
||||
isDev: this.isDev,
|
||||
embark: this.embark
|
||||
});
|
||||
|
||||
this.blockchainProcess.startBlockchainNode(callback);
|
||||
}
|
||||
|
||||
stopBlockchainNode(cb) {
|
||||
const message = __(`The blockchain process has been stopped. It can be restarted by running ${"service blockchain on".bold} in the Embark console.`);
|
||||
|
||||
if (!this.blockchainProcess) {
|
||||
return cb();
|
||||
}
|
||||
|
||||
this.blockchainProcess.stopBlockchainNode(() => {
|
||||
this.logger.info(message);
|
||||
cb();
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module.exports = Nethermind;
|
|
@ -0,0 +1,249 @@
|
|||
import {__} from 'embark-i18n';
|
||||
const path = require('path');
|
||||
const async = require('async');
|
||||
const semver = require('semver');
|
||||
|
||||
const DEFAULTS = {
|
||||
"BIN": "Nethermind.Runner",
|
||||
"VERSIONS_SUPPORTED": ">=1.4.0",
|
||||
"NETWORK_TYPE": "custom",
|
||||
"NETWORK_ID": 1337,
|
||||
"RPC_API": ['eth', 'web3', 'net', 'debug', 'personal'],
|
||||
"WS_API": ['eth', 'web3', 'net', 'debug', 'pubsub', 'personal'],
|
||||
"DEV_WS_API": ['eth', 'web3', 'net', 'debug', 'pubsub', 'personal'],
|
||||
"TARGET_GAS_LIMIT": 8000000
|
||||
};
|
||||
|
||||
const NETHERMIND_NAME = 'nethermind';
|
||||
|
||||
class NethermindClient {
|
||||
|
||||
static get DEFAULTS() {
|
||||
return DEFAULTS;
|
||||
}
|
||||
|
||||
constructor(options) {
|
||||
this.logger = options.logger;
|
||||
this.config = options.hasOwnProperty('config') ? options.config : {};
|
||||
this.env = options.hasOwnProperty('env') ? options.env : 'development';
|
||||
this.isDev = options.hasOwnProperty('isDev') ? options.isDev : (this.env === 'development');
|
||||
this.name = NETHERMIND_NAME;
|
||||
this.prettyName = "Nethermind (https://github.com/NethermindEth/nethermind)";
|
||||
this.bin = this.config.ethereumClientBin || DEFAULTS.BIN;
|
||||
this.versSupported = DEFAULTS.VERSIONS_SUPPORTED;
|
||||
}
|
||||
|
||||
isReady(data) {
|
||||
return data.indexOf('Running server, url:') > -1;
|
||||
}
|
||||
|
||||
commonOptions() {
|
||||
const config = this.config;
|
||||
const cmd = [];
|
||||
|
||||
cmd.push(this.determineNetworkType(config));
|
||||
|
||||
if (config.datadir) {
|
||||
// There isn't a real data dir, so at least we put the keys there
|
||||
cmd.push(`--KeyStore.KeyStoreDirectory=${config.datadir}`);
|
||||
}
|
||||
|
||||
if (config.syncMode === 'light') {
|
||||
this.logger.warn('Light sync mode does not exist in Nethermind. Switching to fast');
|
||||
cmd.push("--Sync.FastSync=true");
|
||||
} else if (config.syncMode === 'fast') {
|
||||
cmd.push("--Sync.FastSync=true");
|
||||
}
|
||||
|
||||
// In dev mode we store all users passwords in the devPassword file, so Parity can unlock all users from the start
|
||||
if (this.isDev && config.account && config.account.numAccounts) {
|
||||
cmd.push(`--Wallet.DevAccounts=${config.account.numAccounts}`);
|
||||
} else if (config.account && config.account.password) {
|
||||
// TODO find a way to see if we can set a password
|
||||
// cmd.push(`--password=${config.account.password}`);
|
||||
}
|
||||
|
||||
// TODO reanable this when the log level is usable
|
||||
// currently, you have to restart the client for the log level to apply and even then, it looks bugged
|
||||
// if (Number.isInteger(config.verbosity) && config.verbosity >= 0 && config.verbosity <= 5) {
|
||||
// switch (config.verbosity) {
|
||||
// case 0:
|
||||
// cmd.push("--log=OFF");
|
||||
// break;
|
||||
// case 1:
|
||||
// cmd.push("--log=ERROR");
|
||||
// break;
|
||||
// case 2:
|
||||
// cmd.push("--log=WARN");
|
||||
// break;
|
||||
// case 3:
|
||||
// cmd.push("--log=INFO");
|
||||
// break;
|
||||
// case 4:
|
||||
// cmd.push("--log=DEBUG");
|
||||
// break;
|
||||
// case 5:
|
||||
// cmd.push("--log=TRACE");
|
||||
// break;
|
||||
// default:
|
||||
// cmd.push("--log=INFO");
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
|
||||
return cmd;
|
||||
}
|
||||
|
||||
getMiner() {
|
||||
console.warn(__("Miner requested, but Nethermind does not embed a miner! Use Geth or install ethminer (https://github.com/ethereum-mining/ethminer)").yellow);
|
||||
return;
|
||||
}
|
||||
|
||||
getBinaryPath() {
|
||||
return this.bin;
|
||||
}
|
||||
|
||||
determineVersionCommand() {
|
||||
let launcher = 'Nethermind.Launcher';
|
||||
if (this.config.ethereumClientBin) {
|
||||
// Replace the Runner by the Launcher in the path
|
||||
// This is done because the Runner does not have a version command, but the Launcher has one ¯\_(ツ)_/¯
|
||||
launcher = this.config.ethereumClientBin.replace(path.basename(this.config.ethereumClientBin), launcher);
|
||||
}
|
||||
return `${launcher} --version`;
|
||||
}
|
||||
|
||||
parseVersion(rawVersionOutput) {
|
||||
let parsed;
|
||||
const match = rawVersionOutput.match(/v([0-9.]+)/);
|
||||
if (match) {
|
||||
parsed = match[1].trim();
|
||||
}
|
||||
return parsed;
|
||||
}
|
||||
|
||||
isSupportedVersion(parsedVersion) {
|
||||
let test;
|
||||
try {
|
||||
let v = semver(parsedVersion);
|
||||
v = `${v.major}.${v.minor}.${v.patch}`;
|
||||
test = semver.Range(this.versSupported).test(semver(v));
|
||||
if (typeof test !== 'boolean') {
|
||||
test = undefined;
|
||||
}
|
||||
} finally {
|
||||
// eslint-disable-next-line no-unsafe-finally
|
||||
return test;
|
||||
}
|
||||
}
|
||||
|
||||
determineNetworkType(config) {
|
||||
if (this.isDev) {
|
||||
return "--config=ndm_consumer_local";
|
||||
}
|
||||
if (config.networkType === 'testnet') {
|
||||
config.networkType = "ropsten";
|
||||
}
|
||||
return "--config=" + config.networkType;
|
||||
}
|
||||
|
||||
determineRpcOptions(config) {
|
||||
let cmd = [];
|
||||
cmd.push("--Network.DiscoveryPort=" + config.port);
|
||||
cmd.push("--JsonRpc.Port=" + config.rpcPort);
|
||||
cmd.push("--JsonRpc.Host=" + config.rpcHost);
|
||||
// Doesn't seem to support changing CORS
|
||||
// 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('==================================');
|
||||
// }
|
||||
return cmd;
|
||||
}
|
||||
|
||||
determineWsOptions(config) {
|
||||
let cmd = [];
|
||||
if (config.wsRPC) {
|
||||
cmd.push("--Init.WebSocketsEnabled=true");
|
||||
}
|
||||
return cmd;
|
||||
}
|
||||
|
||||
mainCommand(address, done) {
|
||||
let self = this;
|
||||
let config = this.config;
|
||||
let rpc_api = this.config.rpcApi;
|
||||
let args = [];
|
||||
async.waterfall([
|
||||
function commonOptions(callback) {
|
||||
let cmd = self.commonOptions();
|
||||
args = args.concat(cmd);
|
||||
callback();
|
||||
},
|
||||
function rpcOptions(callback) {
|
||||
let cmd = self.determineRpcOptions(self.config);
|
||||
args = args.concat(cmd);
|
||||
callback();
|
||||
},
|
||||
function wsOptions(callback) {
|
||||
let cmd = self.determineWsOptions(self.config);
|
||||
args = args.concat(cmd);
|
||||
callback();
|
||||
},
|
||||
function dontGetPeers(callback) {
|
||||
if (config.nodiscover) {
|
||||
args.push("--Init.DiscoveryEnabled=false");
|
||||
return callback();
|
||||
}
|
||||
callback();
|
||||
},
|
||||
function vmDebug(callback) {
|
||||
if (config.vmdebug) {
|
||||
args.push("----Init.StoreTraces=true");
|
||||
return callback();
|
||||
}
|
||||
callback();
|
||||
},
|
||||
function maxPeers(callback) {
|
||||
args.push("--Network.ActivePeersMaxCount=" + config.maxpeers);
|
||||
callback();
|
||||
},
|
||||
function bootnodes(callback) {
|
||||
if (config.bootnodes && config.bootnodes !== "") {
|
||||
args.push("--Discovery.Bootnodes=" + config.bootnodes);
|
||||
return callback();
|
||||
}
|
||||
callback();
|
||||
},
|
||||
function rpcApi(callback) {
|
||||
args.push('--JsonRpc.EnabledModules=' + rpc_api.join(','));
|
||||
callback();
|
||||
},
|
||||
function customOptions(callback) {
|
||||
if (config.customOptions) {
|
||||
if (Array.isArray(config.customOptions)) {
|
||||
config.customOptions = config.customOptions.join(' ');
|
||||
}
|
||||
args.push(config.customOptions);
|
||||
return callback();
|
||||
}
|
||||
callback();
|
||||
}
|
||||
], function (err) {
|
||||
if (err) {
|
||||
throw new Error(err.message);
|
||||
}
|
||||
return done(self.bin, args);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = NethermindClient;
|
|
@ -0,0 +1,23 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"composite": true,
|
||||
"declarationDir": "./dist",
|
||||
"rootDir": "./src",
|
||||
"tsBuildInfoFile": "./node_modules/.cache/tsc/tsconfig.embark-nethermind.tsbuildinfo"
|
||||
},
|
||||
"extends": "../../../tsconfig.base.json",
|
||||
"include": [
|
||||
"src/**/*"
|
||||
],
|
||||
"references": [
|
||||
{
|
||||
"path": "../../core/core"
|
||||
},
|
||||
{
|
||||
"path": "../../core/i18n"
|
||||
},
|
||||
{
|
||||
"path": "../../core/utils"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -36,7 +36,7 @@ class Blockchain {
|
|||
this.config = {
|
||||
silent: this.userConfig.silent,
|
||||
client: this.userConfig.client,
|
||||
ethereumClientBin: this.userConfig.ethereumClientBin || this.userConfig.client,
|
||||
ethereumClientBin: this.userConfig.ethereumClientBin,
|
||||
networkType: this.userConfig.networkType || clientClass.DEFAULTS.NETWORK_TYPE,
|
||||
networkId: this.userConfig.networkId || clientClass.DEFAULTS.NETWORK_ID,
|
||||
genesisBlock: this.userConfig.genesisBlock || false,
|
||||
|
|
|
@ -11,6 +11,7 @@ class Parity {
|
|||
this.embark = embark;
|
||||
this.embarkConfig = embark.config.embarkConfig;
|
||||
this.blockchainConfig = embark.config.blockchainConfig;
|
||||
// TODO get options from config instead of options
|
||||
this.locale = options.locale;
|
||||
this.logger = embark.logger;
|
||||
this.client = options.client;
|
||||
|
|
|
@ -33,7 +33,7 @@ class Blockchain {
|
|||
this.config = {
|
||||
silent: this.userConfig.silent,
|
||||
client: this.userConfig.client,
|
||||
ethereumClientBin: this.userConfig.ethereumClientBin || this.userConfig.client,
|
||||
ethereumClientBin: this.userConfig.ethereumClientBin,
|
||||
networkType: this.userConfig.networkType || clientClass.DEFAULTS.NETWORK_TYPE,
|
||||
networkId: this.userConfig.networkId || clientClass.DEFAULTS.NETWORK_ID,
|
||||
genesisBlock: this.userConfig.genesisBlock || false,
|
||||
|
|
|
@ -85,6 +85,9 @@
|
|||
{
|
||||
"path": "packages/plugins/mocha-tests"
|
||||
},
|
||||
{
|
||||
"path": "packages/plugins/nethermind"
|
||||
},
|
||||
{
|
||||
"path": "packages/plugins/parity"
|
||||
},
|
||||
|
|
Loading…
Reference in New Issue