mirror of https://github.com/embarklabs/embark.git
373 lines
12 KiB
JavaScript
373 lines
12 KiB
JavaScript
const async = require('async');
|
|
const fs = require('../../core/fs.js');
|
|
const os = require('os');
|
|
const semver = require('semver');
|
|
|
|
const DEFAULTS = {
|
|
"BIN": "parity",
|
|
"VERSIONS_SUPPORTED": ">=2.0.0",
|
|
"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;
|
|
this.versSupported = DEFAULTS.VERSIONS_SUPPORTED;
|
|
}
|
|
|
|
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";
|
|
}
|
|
|
|
parseVersion(rawVersionOutput) {
|
|
let parsed;
|
|
const match = rawVersionOutput.match(/version Parity-Ethereum\/(.*?)\//);
|
|
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 "--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;
|