mirror of
https://github.com/embarklabs/embark.git
synced 2025-02-19 17:14:40 +00:00
WIP SAVEPOINT
This commit is contained in:
parent
8df6aec8f7
commit
df20cd325e
@ -1,11 +1,12 @@
|
||||
{
|
||||
"default": {
|
||||
"enabled": true,
|
||||
"client": "geth",
|
||||
"provider": "whisper",
|
||||
"enabled": true,
|
||||
"available_providers": ["whisper"],
|
||||
"connection": {
|
||||
"host": "localhost",
|
||||
"port": 8546,
|
||||
"port": 3777,
|
||||
"type": "ws"
|
||||
}
|
||||
}
|
||||
|
@ -201,7 +201,7 @@ Blockchain.prototype.run = function () {
|
||||
args = compact(args);
|
||||
|
||||
let full_cmd = cmd + " " + args.join(' ');
|
||||
self.logger.info(__("running: %s", full_cmd.underline).green);
|
||||
self.logger.info(__(">>>>>>>>>>>>>>>>> running: %s", full_cmd.underline).green);
|
||||
self.child = spawn(cmd, args, {cwd: process.cwd()});
|
||||
|
||||
self.child.on('error', (err) => {
|
||||
|
@ -13,7 +13,6 @@ class API {
|
||||
this.embark = embark;
|
||||
this.logger = embark.logger;
|
||||
this.web3 = web3;
|
||||
this.registerAPICalls();
|
||||
}
|
||||
|
||||
registerAPICalls() {
|
||||
|
@ -17,37 +17,44 @@ class Whisper {
|
||||
this.webSocketsChannels = {};
|
||||
this.modulesPath = dappPath(embark.config.embarkConfig.generationDir, constants.dappArtifacts.symlinkDir);
|
||||
|
||||
this.api = new API(embark, this.web3);
|
||||
this.api.registerAPICalls();
|
||||
this.whisperNodes = {};
|
||||
|
||||
this.events.setCommandHandler("whisper:node:register", (clientName, startCb) => {
|
||||
this.whisperNodes[clientName] = startCb;
|
||||
});
|
||||
|
||||
this.events.request("communication:node:register", "whisper", (readyCb) => {
|
||||
let clientName = this.communicationConfig.client;
|
||||
let registerCb = this.whisperNodes[clientName];
|
||||
registerCb.apply(registerCb, [readyCb]);
|
||||
});
|
||||
|
||||
this.events.request("runcode:whitelist", 'embarkjs', () => { });
|
||||
this.events.request("runcode:whitelist", 'embarkjs-whisper', () => { });
|
||||
|
||||
// TODO: should launch its own whisper node
|
||||
// this.events.on("communication:started", this.connectEmbarkJSProvider.bind(this));
|
||||
this.events.on("blockchain:started", this.connectEmbarkJSProvider.bind(this));
|
||||
|
||||
embark.registerActionForEvent("pipeline:generateAll:before", this.addEmbarkJSWhisperArtifact.bind(this));
|
||||
|
||||
this.events.request("communication:node:register", "whisper", (readyCb) => {
|
||||
// TODO: should launch its own whisper node
|
||||
console.dir("--- whisper readyCb")
|
||||
console.dir('--- registering whisper node')
|
||||
// this.events.request('processes:register', 'communication', {
|
||||
// launchFn: (cb) => {
|
||||
// this.startProcess(cb);
|
||||
// },
|
||||
// stopFn: (cb) => { this.stopProcess(cb); }
|
||||
// });
|
||||
// this.events.request("processes:launch", "communication", (err) => {
|
||||
readyCb()
|
||||
// });
|
||||
// this.registerServiceCheck()
|
||||
this.events.on("communication:started", () => {
|
||||
this.setWhisperProvider();
|
||||
this.api = new API(embark, this.web3);
|
||||
this.api.registerAPICalls();
|
||||
this.connectEmbarkJSProvider.bind(this)
|
||||
});
|
||||
|
||||
let plugin = this.plugins.createPlugin('whisperplugin', {});
|
||||
plugin.registerActionForEvent("pipeline:generateAll:before", this.addEmbarkJSWhisperArtifact.bind(this));
|
||||
this.registerEmbarkJSCommunication()
|
||||
}
|
||||
|
||||
setWhisperProvider() {
|
||||
let {host, port} = this.communicationConfig.connection;
|
||||
let web3Endpoint = 'ws://' + host + ':' + port;
|
||||
// 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: constants.embarkResourceOrigin}}));
|
||||
}
|
||||
|
||||
async addEmbarkJSWhisperArtifact(params, cb) {
|
||||
let connection = this.communicationConfig.connection || {};
|
||||
const config = {
|
||||
|
@ -162,6 +162,7 @@ class EmbarkController {
|
||||
engine.registerModuleGroup("stackComponents");
|
||||
|
||||
// TODO: replace with individual plugins
|
||||
engine.registerModuleGroup("communication");
|
||||
engine.registerModuleGroup("blockchain");
|
||||
engine.registerModuleGroup("compiler");
|
||||
engine.registerModuleGroup("contracts");
|
||||
@ -169,7 +170,6 @@ class EmbarkController {
|
||||
engine.registerModuleGroup("webserver");
|
||||
engine.registerModuleGroup("filewatcher");
|
||||
engine.registerModuleGroup("storage");
|
||||
engine.registerModuleGroup("communication");
|
||||
engine.registerModuleGroup("cockpit");
|
||||
engine.registerModuleGroup("namesystem");
|
||||
engine.registerModulePackage('embark-deploy-tracker', {plugins: engine.plugins});
|
||||
@ -192,7 +192,6 @@ class EmbarkController {
|
||||
} catch (e) {
|
||||
return cb(e);
|
||||
}
|
||||
|
||||
cb();
|
||||
});
|
||||
|
||||
|
@ -5,6 +5,7 @@ const {spawn, exec} = require('child_process');
|
||||
const path = require('path');
|
||||
const constants = require('embark-core/constants');
|
||||
const GethClient = require('./gethClient.js');
|
||||
const WhisperGethClient = require('./whisperClient.js');
|
||||
// const ParityClient = require('./parityClient.js');
|
||||
import { IPC } from 'embark-core';
|
||||
|
||||
@ -15,7 +16,7 @@ const Logger = require('embark-logger');
|
||||
const IPC_CONNECT_INTERVAL = 2000;
|
||||
|
||||
/*eslint complexity: ["error", 50]*/
|
||||
var Blockchain = function(userConfig, clientClass) {
|
||||
var Blockchain = function(userConfig, clientClass, communicationConfig) {
|
||||
this.userConfig = userConfig;
|
||||
this.env = userConfig.env || 'development';
|
||||
this.isDev = userConfig.isDev;
|
||||
@ -100,8 +101,7 @@ var Blockchain = function(userConfig, clientClass) {
|
||||
this.logger.error(__(spaceMessage, 'genesisBlock'));
|
||||
process.exit(1);
|
||||
}
|
||||
this.client = new clientClass({config: this.config, env: this.env, isDev: this.isDev});
|
||||
|
||||
this.client = new clientClass({config: this.config, env: this.env, isDev: this.isDev, communicationConfig: communicationConfig});
|
||||
this.initStandaloneProcess();
|
||||
};
|
||||
|
||||
@ -201,7 +201,7 @@ Blockchain.prototype.run = function () {
|
||||
args = compact(args);
|
||||
|
||||
let full_cmd = cmd + " " + args.join(' ');
|
||||
self.logger.info(__("running: %s", full_cmd.underline).green);
|
||||
self.logger.info(__(">>>>>>>>>>>>>>>> running: %s", full_cmd.underline).green);
|
||||
self.child = spawn(cmd, args, {cwd: process.cwd()});
|
||||
|
||||
self.child.on('error', (err) => {
|
||||
@ -413,7 +413,7 @@ Blockchain.prototype.initChainAndGetAddress = function (callback) {
|
||||
});
|
||||
};
|
||||
|
||||
export function BlockchainClient(userConfig, options) {
|
||||
export function BlockchainClient(userConfig, options, communicationConfig) {
|
||||
if ((userConfig === {} || JSON.stringify(userConfig) === '{"enabled":true}') && options.env !== 'development') {
|
||||
options.logger.info("===> " + __("warning: running default config on a non-development environment"));
|
||||
}
|
||||
@ -423,18 +423,13 @@ export function BlockchainClient(userConfig, options) {
|
||||
if (options.clientName) userConfig.client = options.clientName;
|
||||
// Choose correct client instance based on clientName
|
||||
let clientClass;
|
||||
switch (userConfig.client) {
|
||||
case constants.blockchain.clients.geth:
|
||||
clientClass = GethClient;
|
||||
break;
|
||||
|
||||
// case constants.blockchain.clients.parity:
|
||||
// clientClass = ParityClient;
|
||||
// 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);
|
||||
if (communicationConfig) {
|
||||
clientClass = WhisperGethClient
|
||||
} else {
|
||||
clientClass = GethClient;
|
||||
}
|
||||
|
||||
userConfig.isDev = (userConfig.isDev || userConfig.default);
|
||||
userConfig.env = options.env;
|
||||
userConfig.onReadyCallback = options.onReadyCallback;
|
||||
@ -442,5 +437,5 @@ export function BlockchainClient(userConfig, options) {
|
||||
userConfig.logger = options.logger;
|
||||
userConfig.certOptions = options.certOptions;
|
||||
userConfig.isStandalone = options.isStandalone;
|
||||
return new Blockchain(userConfig, clientClass);
|
||||
return new Blockchain(userConfig, clientClass, communicationConfig);
|
||||
}
|
||||
|
@ -9,6 +9,7 @@ class BlockchainProcess extends ProcessWrapper {
|
||||
constructor(options) {
|
||||
super();
|
||||
this.blockchainConfig = options.blockchainConfig;
|
||||
this.communicationConfig = options.communicationConfig;
|
||||
this.client = options.client;
|
||||
this.env = options.env;
|
||||
this.isDev = options.isDev;
|
||||
@ -26,7 +27,8 @@ class BlockchainProcess extends ProcessWrapper {
|
||||
onReadyCallback: this.blockchainReady.bind(this),
|
||||
onExitCallback: this.blockchainExit.bind(this),
|
||||
logger: console
|
||||
}
|
||||
},
|
||||
this.communicationConfig
|
||||
);
|
||||
|
||||
this.blockchain.run();
|
||||
|
@ -10,6 +10,7 @@ export class BlockchainProcessLauncher {
|
||||
this.logger = options.logger;
|
||||
this.normalizeInput = options.normalizeInput;
|
||||
this.blockchainConfig = options.blockchainConfig;
|
||||
this.communicationConfig = options.communicationConfig;
|
||||
this.locale = options.locale;
|
||||
this.isDev = options.isDev;
|
||||
this.client = options.client;
|
||||
@ -35,6 +36,7 @@ export class BlockchainProcessLauncher {
|
||||
this.blockchainProcess.send({
|
||||
action: constants.blockchain.init, options: {
|
||||
blockchainConfig: this.blockchainConfig,
|
||||
communicationConfig: this.communicationConfig,
|
||||
client: this.client,
|
||||
env: this.env,
|
||||
isDev: this.isDev,
|
||||
|
@ -9,6 +9,7 @@ class Geth {
|
||||
this.embark = embark;
|
||||
this.embarkConfig = embark.config.embarkConfig;
|
||||
this.blockchainConfig = embark.config.blockchainConfig;
|
||||
this.communicationConfig = embark.config.communicationConfig;
|
||||
this.locale = options.locale;
|
||||
this.logger = embark.logger;
|
||||
this.client = options.client;
|
||||
@ -23,7 +24,6 @@ class Geth {
|
||||
this.events.request("blockchain:node:register", constants.blockchain.clients.geth, (readyCb) => {
|
||||
this.events.request('processes:register', 'blockchain', {
|
||||
launchFn: (cb) => {
|
||||
// this.startBlockchainNode(readyCb);
|
||||
this.startBlockchainNode(cb);
|
||||
},
|
||||
stopFn: (cb) => {
|
||||
@ -38,6 +38,24 @@ class Geth {
|
||||
});
|
||||
this.registerServiceCheck();
|
||||
});
|
||||
|
||||
this.events.request("whisper:node:register", constants.blockchain.clients.geth, readyCb => {
|
||||
this.events.request('processes:register', 'communication', {
|
||||
launchFn: cb => {
|
||||
this.startWhisperNode(cb);
|
||||
},
|
||||
stopFn: cb => {
|
||||
this.stopWhisperNode(cb);
|
||||
}
|
||||
});
|
||||
|
||||
this.events.request("processes:launch", "communication", (err) => {
|
||||
if (err) {
|
||||
this.logger.error(`Error launching whisper process: ${err.message || err}`);
|
||||
}
|
||||
readyCb();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
shouldInit() {
|
||||
@ -82,6 +100,31 @@ class Geth {
|
||||
this.blockchainProcess.startBlockchainNode(callback);
|
||||
}
|
||||
|
||||
startWhisperNode(callback) {
|
||||
this.whisperProcess = new BlockchainProcessLauncher({
|
||||
events: this.events,
|
||||
logger: this.logger,
|
||||
normalizeInput,
|
||||
blockchainConfig: this.blockchainConfig,
|
||||
communicationConfig: this.communicationConfig,
|
||||
locale: this.locale,
|
||||
client: this.client,
|
||||
isDev: this.isDev,
|
||||
embark: this.embark
|
||||
});
|
||||
this.whisperProcess.startBlockchainNode(callback);
|
||||
}
|
||||
|
||||
stopWhisperNode(cb) {
|
||||
if (!this.whisperProcess) {
|
||||
return cb();
|
||||
}
|
||||
this.whisperProcess.stopBlockchainNode(() => {
|
||||
this.logger.info(`The whisper process has been stopped.`);
|
||||
cb();
|
||||
});
|
||||
}
|
||||
|
||||
stopBlockchainNode(cb) {
|
||||
const message = __(`The blockchain process has been stopped. It can be restarted by running ${"service blockchain on".bold} in the Embark console.`);
|
||||
|
||||
|
261
packages/embark/src/lib/modules/geth/whisperClient.js
Normal file
261
packages/embark/src/lib/modules/geth/whisperClient.js
Normal file
@ -0,0 +1,261 @@
|
||||
import { __ } from 'embark-i18n';
|
||||
import { dappPath, ipcPath } from 'embark-utils';
|
||||
const async = require('async');
|
||||
const {exec, spawn} = require('child_process');
|
||||
const path = require('path');
|
||||
const GethMiner = require('./miner');
|
||||
const semver = require('semver');
|
||||
const constants = require('embark-core/constants');
|
||||
|
||||
const DEFAULTS = {
|
||||
"BIN": "geth",
|
||||
"VERSIONS_SUPPORTED": ">=1.8.14",
|
||||
"NETWORK_TYPE": "custom",
|
||||
"NETWORK_ID": 1337,
|
||||
"RPC_API": ['eth', 'web3', 'net', 'debug', 'personal'],
|
||||
"WS_API": ['eth', 'web3', 'net', 'shh', 'debug', 'pubsub', 'personal'],
|
||||
"DEV_WS_API": ['eth', 'web3', 'net', 'shh', 'debug', 'pubsub', 'personal'],
|
||||
"TARGET_GAS_LIMIT": 8000000
|
||||
};
|
||||
|
||||
class WhisperGethClient {
|
||||
|
||||
static get DEFAULTS() {
|
||||
return DEFAULTS;
|
||||
}
|
||||
|
||||
constructor(options) {
|
||||
this.config = options && options.hasOwnProperty('config') ? options.config : {};
|
||||
this.communicationConfig = options.communicationConfig;
|
||||
this.env = options && options.hasOwnProperty('env') ? options.env : 'development';
|
||||
this.isDev = options && options.hasOwnProperty('isDev') ? options.isDev : (this.env === 'development');
|
||||
this.name = constants.blockchain.clients.geth;
|
||||
this.prettyName = "Go-Ethereum (https://github.com/ethereum/go-ethereum)";
|
||||
this.bin = this.config.ethereumClientBin || DEFAULTS.BIN;
|
||||
this.versSupported = DEFAULTS.VERSIONS_SUPPORTED;
|
||||
this.httpReady = false;
|
||||
this.wsReady = !this.config.wsRPC;
|
||||
}
|
||||
|
||||
isReady(data) {
|
||||
if (data.indexOf('WebSocket endpoint opened') > -1) {
|
||||
this.wsReady = true;
|
||||
}
|
||||
return 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() {
|
||||
return false;
|
||||
}
|
||||
|
||||
commonOptions() {
|
||||
return [];
|
||||
}
|
||||
|
||||
getMiner() {
|
||||
return new GethMiner({datadir: this.config.datadir});
|
||||
}
|
||||
|
||||
getBinaryPath() {
|
||||
return this.bin;
|
||||
}
|
||||
|
||||
determineVersionCommand() {
|
||||
return this.bin + " version";
|
||||
}
|
||||
|
||||
parseVersion(rawVersionOutput) {
|
||||
let parsed;
|
||||
const match = rawVersionOutput.match(/Version: (.*)/);
|
||||
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) {
|
||||
let cmd;
|
||||
if (config.networkType === 'testnet') {
|
||||
cmd = "--testnet";
|
||||
} else if (config.networkType === 'rinkeby') {
|
||||
cmd = "--rinkeby";
|
||||
} else if (config.networkType === 'custom') {
|
||||
cmd = "--networkid=" + config.networkId;
|
||||
}
|
||||
return cmd;
|
||||
}
|
||||
|
||||
runAsArchival(config) {
|
||||
return config.networkId === 1337 || config.archivalMode;
|
||||
}
|
||||
|
||||
initGenesisCommmand() {
|
||||
return "";
|
||||
}
|
||||
|
||||
newAccountCommand() {
|
||||
return "";
|
||||
}
|
||||
|
||||
parseNewAccountCommandResultToAddress(data = "") {
|
||||
if (data.match(/{(\w+)}/)) return "0x" + data.match(/{(\w+)}/)[1];
|
||||
return "";
|
||||
}
|
||||
|
||||
listAccountsCommand() {
|
||||
return "";
|
||||
}
|
||||
|
||||
parseListAccountsCommandResultToAddress(data = "") {
|
||||
if (data.match(/{(\w+)}/)) return "0x" + data.match(/{(\w+)}/)[1];
|
||||
return "";
|
||||
}
|
||||
|
||||
parseListAccountsCommandResultToAddressList(data = "") {
|
||||
const regex = RegExp(/{(\w+)}/g);
|
||||
let match;
|
||||
const accounts = [];
|
||||
while ((match = regex.exec(data)) !== null) {
|
||||
accounts.push('0x' + match[1]);
|
||||
}
|
||||
return accounts;
|
||||
}
|
||||
|
||||
parseListAccountsCommandResultToAddressCount(data = "") {
|
||||
return 0;
|
||||
}
|
||||
|
||||
determineRpcOptions(config) {
|
||||
let cmd = [];
|
||||
cmd.push("--port=30304");
|
||||
cmd.push("--rpc");
|
||||
cmd.push("--rpcport=9998");
|
||||
cmd.push("--rpcaddr=" + config.rpcHost);
|
||||
|
||||
if (config.rpcCorsDomain) {
|
||||
if (config.rpcCorsDomain === '*') {
|
||||
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.warn('==================================');
|
||||
console.warn(__('warning: cors is not set'));
|
||||
console.warn('==================================');
|
||||
}
|
||||
return cmd;
|
||||
}
|
||||
|
||||
determineWsOptions(config, communicationConfig) {
|
||||
let cmd = [];
|
||||
if (config.wsRPC) {
|
||||
cmd.push("--ws");
|
||||
|
||||
cmd.push(`--wsport=${communicationConfig.connection.port || config.wsPost++}`);
|
||||
cmd.push(`--wsaddr=${communicationConfig.connection.host || config.wsHost++}`);
|
||||
|
||||
if (config.wsOrigins) {
|
||||
if (config.wsOrigins === '*') {
|
||||
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.warn('==================================');
|
||||
console.warn(__('warning: wsOrigins is not set'));
|
||||
console.warn('==================================');
|
||||
}
|
||||
}
|
||||
return cmd;
|
||||
}
|
||||
|
||||
initDevChain(datadir, callback) {
|
||||
callback();
|
||||
}
|
||||
|
||||
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 wsOptions(callback) {
|
||||
let cmd = self.determineWsOptions(self.config, self.communicationConfig);
|
||||
args = args.concat(cmd);
|
||||
callback(null, cmd);
|
||||
},
|
||||
function dontGetPeers(callback) {
|
||||
if (config.nodiscover) {
|
||||
args.push("--nodiscover");
|
||||
return callback(null, "--nodiscover");
|
||||
}
|
||||
callback(null, "");
|
||||
},
|
||||
function maxPeers(callback) {
|
||||
let cmd = "--maxpeers=" + 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) {
|
||||
rpc_api.push('shh');
|
||||
if (ws_api.indexOf('shh') === -1) {
|
||||
ws_api.push('shh');
|
||||
}
|
||||
args.push("--shh");
|
||||
return callback(null, "--shh ");
|
||||
},
|
||||
function rpcApi(callback) {
|
||||
args.push('--rpcapi=' + rpc_api.join(','));
|
||||
callback(null, '--rpcapi=' + rpc_api.join(','));
|
||||
},
|
||||
function wsApi(callback) {
|
||||
args.push('--wsapi=' + ws_api.join(','));
|
||||
callback(null, '--wsapi=' + ws_api.join(','));
|
||||
}
|
||||
], function(err) {
|
||||
if (err) {
|
||||
throw new Error(err.message);
|
||||
}
|
||||
return done(self.bin, args);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = WhisperGethClient;
|
||||
|
Loading…
x
Reference in New Issue
Block a user