2018-10-06 16:05:37 +00:00
const async = require ( 'async' ) ;
const fs = require ( '../../core/fs.js' ) ;
const os = require ( 'os' ) ;
2018-10-29 13:42:53 +00:00
const semver = require ( 'semver' ) ;
2018-10-06 16:05:37 +00:00
const DEFAULTS = {
"BIN" : "parity" ,
2018-10-29 13:42:53 +00:00
"VERSIONS_SUPPORTED" : ">=2.0.0" ,
2018-10-06 16:05:37 +00:00
"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 ;
2018-10-29 13:42:53 +00:00
this . versSupported = DEFAULTS . VERSIONS _SUPPORTED ;
2018-10-06 16:05:37 +00:00
}
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" ;
}
2018-10-29 13:42:53 +00:00
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 ;
}
}
2018-10-06 16:05:37 +00:00
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 ;