const electron = require ( 'electron' ) ;
const app = electron . app ;
const BrowserWindow = electron . BrowserWindow ;
const path = require ( 'path' ) ;
const url = require ( 'url' ) ;
const os = require ( 'os' ) ;
const fsnode = require ( 'fs' ) ;
const fs = require ( 'fs-extra' ) ;
const _ fs = require ( 'graceful-fs' ) ;
const express = require ( 'express' ) ;
const exec = require ( 'child_process' ) . exec ;
const spawn = require ( 'child_process' ) . spawn ;
const md5 = require ( './md5.js' ) ;
const request = require ( 'request' ) ;
const async = require ( 'async' ) ;
const portscanner = require ( 'portscanner' ) ;
const aes256 = require ( 'nodejs-aes256' ) ;
const AdmZip = require ( 'adm-zip' ) ;
const remoteFileSize = require ( 'remote-file-size' ) ;
const Promise = require ( 'bluebird' ) ;
const { shell } = require ( 'electron' ) ;
const { execFile } = require ( 'child_process' ) ;
const sha256 = require ( 'sha256' ) ;
const CoinKey = require ( 'coinkey' ) ;
const bitcoinJS = require ( 'bitcoinjs-lib' ) ;
const coinSelect = require ( 'coinselect' ) ;
const fixPath = require ( 'fix-path' ) ;
const crypto = require ( 'crypto' ) ;
var ps = require ( 'ps-node' ) ;
var setconf = require ( '../private/setconf.js' ) ;
var nativeCoind = require ( './nativeCoind.js' ) ;
var assetChainPorts = require ( './ports.js' ) ;
var _ appConfig = require ( './appConfig.js' ) ;
var shepherd = express . Router ( ) ;
var coindInstanceRegistry = { } ;
var guiLog = { } ;
var rpcConf = { } ;
var appRuntimeLog = [ ] ;
var lockDownAddCoin = false ;
var electrumCoins = {
auth : false ,
} ;
var electrumKeys = { } ;
const electrumJSCore = require ( './electrumjs/electrumjs.core.js' ) ;
const electrumJSNetworks = require ( './electrumjs/electrumjs.networks.js' ) ;
const electrumJSTxDecoder = require ( './electrumjs/electrumjs.txdecoder.js' ) ;
let electrumServers = {
/ * z c a s h : {
address : '173.212.225.176' ,
port : 50032 ,
proto : 'tcp' ,
} , * /
revs : { // !estimatefee
address : '173.212.225.176' ,
port : 50050 ,
proto : 'tcp' ,
txfee : 10000 ,
abbr : 'REVS' ,
serverList : [
'173.212.225.176:50050' ,
'136.243.45.140:50050'
] ,
} ,
mnz : { // !estimatefee
address : '173.212.225.176' ,
port : 50053 ,
proto : 'tcp' ,
txfee : 10000 ,
abbr : 'MNZ' ,
serverList : [
'173.212.225.176:50053' ,
'136.243.45.140:50053'
] ,
} ,
wlc : { // !estimatefee
address : '173.212.225.176' ,
port : 50052 ,
proto : 'tcp' ,
txfee : 10000 ,
abbr : 'WLC' ,
serverList : [
'173.212.225.176:50052' ,
'136.243.45.140:50052'
] ,
} ,
jumblr : { // !estimatefee
address : '173.212.225.176' ,
port : 50051 ,
proto : 'tcp' ,
txfee : 10000 ,
abbr : 'JUMBLR' ,
serverList : [
'173.212.225.176:50051' ,
'136.243.45.140:50051'
] ,
} ,
komodo : { // !estimatefee
address : '173.212.225.176' ,
port : 50011 ,
proto : 'tcp' ,
txfee : 10000 ,
abbr : 'KMD' ,
serverList : [
'173.212.225.176:50011' ,
'136.243.45.140:50011'
] ,
} ,
dogecoin : { // !estimatefee
address : '173.212.225.176' ,
port : 50015 ,
proto : 'tcp' ,
txfee : 100000000 ,
abbr : 'DOGE' ,
} ,
viacoin : { // !estimatefee
address : 'vialectrum.bitops.me' ,
port : 50002 ,
proto : 'ssl' ,
txfee : 100000 ,
abbr : 'VIA' ,
} ,
vertcoin : {
address : '173.212.225.176' ,
port : 50088 ,
proto : 'tcp' ,
txfee : 100000 ,
abbr : 'VTC' ,
} ,
namecoin : {
address : '173.212.225.176' ,
port : 50036 ,
proto : 'tcp' ,
txfee : 100000 ,
abbr : 'NMC' ,
} ,
monacoin : { // !estimatefee
address : '173.212.225.176' ,
port : 50002 ,
proto : 'tcp' ,
txfee : 100000 ,
abbr : 'MONA' ,
} ,
litecoin : {
address : '173.212.225.176' ,
port : 50012 ,
proto : 'tcp' ,
txfee : 10000 ,
abbr : 'LTC' ,
} ,
faircoin : {
address : '173.212.225.176' ,
port : 50005 ,
proto : 'tcp' ,
txfee : 1000000 ,
abbr : 'FAIR' ,
} ,
digibyte : {
address : '173.212.225.176' ,
port : 50022 ,
proto : 'tcp' ,
txfee : 100000 ,
abbr : 'DGB' ,
} ,
dash : {
address : '173.212.225.176' ,
port : 50098 ,
proto : 'tcp' ,
txfee : 10000 ,
abbr : 'DASH' ,
} ,
crown : {
address : '173.212.225.176' ,
port : 50041 ,
proto : 'tcp' ,
txfee : 10000 ,
abbr : 'CRW' ,
} ,
bitcoin : {
address : '173.212.225.176' ,
port : 50001 ,
proto : 'tcp' ,
abbr : 'BTC' ,
} ,
argentum : { // !estimatefee
address : '173.212.225.176' ,
port : 50081 ,
proto : 'tcp' ,
txfee : 50000 ,
abbr : 'ARG' ,
} ,
chips : { // !estimatefee
address : '173.212.225.176' ,
port : 50076 ,
proto : 'tcp' ,
txfee : 10000 ,
abbr : 'CHIPS' ,
serverList : [
'173.212.225.176:50076' ,
'136.243.45.140:50076'
] ,
} ,
} ;
const zcashParamsDownloadLinks = {
'agama.komodoplatform.com' : {
proving : 'https://agama.komodoplatform.com/file/supernet/sprout-proving.key' ,
verifying : 'https://agama.komodoplatform.com/file/supernet/sprout-verifying.key' ,
} ,
'zcash.dl.mercerweiss.com' : {
proving : 'https://zcash.dl.mercerweiss.com/sprout-proving.key' ,
verifying : 'https://zcash.dl.mercerweiss.com/sprout-verifying.key' ,
} ,
} ;
shepherd . zcashParamsDownloadLinks = zcashParamsDownloadLinks ;
const CONNECTION_ERROR_OR_INCOMPLETE_DATA = 'connection error or incomplete data' ;
shepherd . appConfig = _ appConfig . config ;
if ( os . platform ( ) === 'darwin' ) {
fixPath ( ) ;
var agamaDir = ` ${ process . env . HOME } /Library/Application Support/Agama ` ;
}
if ( os . platform ( ) === 'linux' ) {
var agamaDir = ` ${ process . env . HOME } /.agama ` ;
}
if ( os . platform ( ) === 'win32' ) {
var agamaDir = ` ${ process . env . APPDATA } /Agama ` ;
agamaDir = path . normalize ( agamaDir ) ;
}
shepherd . log = function ( msg ) {
if ( shepherd . appConfig . dev ||
shepherd . appConfig . debug ) {
console . log ( msg ) ;
}
appRuntimeLog . push ( {
time : Date . now ( ) ,
msg : msg ,
} ) ;
} ;
shepherd . writeLog = function ( data ) {
const logLocation = ` ${ agamaDir } /shepherd ` ;
const timeFormatted = new Date ( Date . now ( ) ) . toLocaleString ( 'en-US' , { hour12 : false } ) ;
if ( shepherd . appConfig . debug ) {
if ( fs . existsSync ( ` ${ logLocation } /agamalog.txt ` ) ) {
fs . appendFile ( ` ${ logLocation } /agamalog.txt ` , ` ${ timeFormatted } ${ data } \r \n ` , function ( err ) {
if ( err ) {
shepherd . log ( 'error writing log file' ) ;
}
} ) ;
} else {
fs . writeFile ( ` ${ logLocation } /agamalog.txt ` , ` ${ timeFormatted } ${ data } \r \n ` , function ( err ) {
if ( err ) {
shepherd . log ( 'error writing log file' ) ;
}
} ) ;
}
}
}
shepherd . loadLocalConfig = function ( ) {
if ( fs . existsSync ( ` ${ agamaDir } /config.json ` ) ) {
let localAppConfig = fs . readFileSync ( ` ${ agamaDir } /config.json ` , 'utf8' ) ;
shepherd . log ( 'app config set from local file' ) ;
shepherd . writeLog ( 'app config set from local file' ) ;
// find diff between local and hardcoded configs
// append diff to local config
const compareJSON = function ( obj1 , obj2 ) {
let result = { } ;
for ( let i in obj1 ) {
if ( ! obj2 . hasOwnProperty ( i ) ) {
result [ i ] = obj1 [ i ] ;
}
}
return result ;
} ;
if ( localAppConfig ) {
const compareConfigs = compareJSON ( shepherd . appConfig , JSON . parse ( localAppConfig ) ) ;
if ( Object . keys ( compareConfigs ) . length ) {
const newConfig = Object . assign ( JSON . parse ( localAppConfig ) , compareConfigs ) ;
shepherd . log ( 'config diff is found, updating local config' ) ;
shepherd . log ( 'config diff:' ) ;
shepherd . log ( compareConfigs ) ;
shepherd . writeLog ( 'aconfig diff is found, updating local config' ) ;
shepherd . writeLog ( 'config diff:' ) ;
shepherd . writeLog ( compareConfigs ) ;
shepherd . saveLocalAppConf ( newConfig ) ;
return newConfig ;
} else {
return JSON . parse ( localAppConfig ) ;
}
} else {
return shepherd . appConfig ;
}
} else {
shepherd . log ( 'local config file is not found!' ) ;
shepherd . writeLog ( 'local config file is not found!' ) ;
shepherd . saveLocalAppConf ( shepherd . appConfig ) ;
return shepherd . appConfig ;
}
} ;
shepherd . saveLocalAppConf = function ( appSettings ) {
let appConfFileName = ` ${ agamaDir } /config.json ` ;
_ fs . access ( agamaDir , fs . constants . R_OK , function ( err ) {
if ( ! err ) {
const FixFilePermissions = function ( ) {
return new Promise ( function ( resolve , reject ) {
const result = 'config.json file permissions updated to Read/Write' ;
fsnode . chmodSync ( appConfFileName , '0666' ) ;
setTimeout ( function ( ) {
shepherd . log ( result ) ;
shepherd . writeLog ( result ) ;
resolve ( result ) ;
} , 1000 ) ;
} ) ;
}
const FsWrite = function ( ) {
return new Promise ( function ( resolve , reject ) {
const result = 'config.json write file is done' ;
fs . writeFile ( appConfFileName ,
JSON . stringify ( appSettings )
. replace ( /,/g , ',\n' ) // format json in human readable form
. replace ( /:/g , ': ' )
. replace ( /{/g , '{\n' )
. replace ( /}/g , '\n}' ) , 'utf8' , function ( err ) {
if ( err )
return shepherd . log ( err ) ;
} ) ;
fsnode . chmodSync ( appConfFileName , '0666' ) ;
setTimeout ( function ( ) {
shepherd . log ( result ) ;
shepherd . log ( ` app conf.json file is created successfully at: ${ agamaDir } ` ) ;
shepherd . writeLog ( ` app conf.json file is created successfully at: ${ agamaDir } ` ) ;
resolve ( result ) ;
} , 2000 ) ;
} ) ;
}
FsWrite ( )
. then ( FixFilePermissions ( ) ) ;
}
} ) ;
}
shepherd . appConfig = shepherd . loadLocalConfig ( ) ;
if ( os . platform ( ) === 'darwin' ) {
fixPath ( ) ;
var agamaTestDir = ` ${ process . env . HOME } /Library/Application Support/Agama/test ` ,
komododBin = path . join ( __ dirname , '../assets/bin/osx/komodod' ) ,
komodocliBin = path . join ( __ dirname , '../assets/bin/osx/komodo-cli' ) ,
komodoDir = shepherd . appConfig . dataDir . length ? shepherd . appConfig . dataDir : ` ${ process . env . HOME } /Library/Application Support/Komodo ` ,
zcashdBin = '/Applications/ZCashSwingWalletUI.app/Contents/MacOS/zcashd' ,
zcashcliBin = '/Applications/ZCashSwingWalletUI.app/Contents/MacOS/zcash-cli' ,
zcashDir = ` ${ process . env . HOME } /Library/Application Support/Zcash ` ,
zcashParamsDir = ` ${ process . env . HOME } /Library/Application Support/ZcashParams ` ,
chipsBin = path . join ( __ dirname , '../assets/bin/osx/chipsd' ) ,
chipscliBin = path . join ( __ dirname , '../assets/bin/osx/chips-cli' ) ,
chipsDir = ` ${ process . env . HOME } /Library/Application Support/Chips ` ,
coindRootDir = path . join ( __ dirname , '../assets/bin/osx/dex/coind' ) ;
}
if ( os . platform ( ) === 'linux' ) {
var agamaTestDir = ` ${ process . env . HOME } /.agama/test ` ,
komododBin = path . join ( __ dirname , '../assets/bin/linux64/komodod' ) ,
komodocliBin = path . join ( __ dirname , '../assets/bin/linux64/komodo-cli' ) ,
komodoDir = shepherd . appConfig . dataDir . length ? shepherd . appConfig . dataDir : ` ${ process . env . HOME } /.komodo ` ,
zcashParamsDir = ` ${ process . env . HOME } /.zcash-params ` ,
chipsBin = path . join ( __ dirname , '../assets/bin/linux64/chipsd' ) ,
chipscliBin = path . join ( __ dirname , '../assets/bin/linux64/chips-cli' ) ,
chipsDir = ` ${ process . env . HOME } /.chips ` ,
coindRootDir = path . join ( __ dirname , '../assets/bin/linux64/dex/coind' ) ;
}
if ( os . platform ( ) === 'win32' ) {
var agamaTestDir = ` ${ process . env . APPDATA } /Agama/test ` ;
agamaTestDir = path . normalize ( agamaTestDir ) ;
komododBin = path . join ( __ dirname , '../assets/bin/win64/komodod.exe' ) ,
komododBin = path . normalize ( komododBin ) ,
komodocliBin = path . join ( __ dirname , '../assets/bin/win64/komodo-cli.exe' ) ,
komodocliBin = path . normalize ( komodocliBin ) ,
komodoDir = shepherd . appConfig . dataDir . length ? shepherd . appConfig . dataDir : ` ${ process . env . APPDATA } /Komodo ` ,
komodoDir = path . normalize ( komodoDir ) ;
chipsBin = path . join ( __ dirname , '../assets/bin/win64/chipsd.exe' ) ,
chipsBin = path . normalize ( chipsBin ) ,
chipscliBin = path . join ( __ dirname , '../assets/bin/win64/chips-cli.exe' ) ,
chipscliBin = path . normalize ( chipscliBin ) ,
chipsDir = ` ${ process . env . APPDATA } /Chips ` ,
chipsDir = path . normalize ( chipsDir ) ;
zcashParamsDir = ` ${ process . env . APPDATA } /ZcashParams ` ;
zcashParamsDir = path . normalize ( zcashParamsDir ) ;
coindRootDir = path . join ( __ dirname , '../assets/bin/osx/dex/coind' ) ;
coindRootDir = path . normalize ( coindRootDir ) ;
}
shepherd . appConfigSchema = _ appConfig . schema ;
shepherd . defaultAppConfig = Object . assign ( { } , shepherd . appConfig ) ;
shepherd . kmdMainPassiveMode = false ;
shepherd . coindInstanceRegistry = coindInstanceRegistry ;
shepherd . getNetworkData = function ( network ) {
const coin = shepherd . findNetworkObj ( network ) || shepherd . findNetworkObj ( network . toUpperCase ( ) ) || shepherd . findNetworkObj ( network . toLowerCase ( ) ) ;
const coinUC = coin ? coin . toUpperCase ( ) : null ;
if ( coin === 'SUPERNET' ||
coin === 'REVS' ||
coin === 'SUPERNET' ||
coin === 'PANGEA' ||
coin === 'DEX' ||
coin === 'JUMBLR' ||
coin === 'BET' ||
coin === 'CRYPTO' ||
coin === 'COQUI' ||
coin === 'HODL' ||
coin === 'SHARK' ||
coin === 'BOTS' ||
coin === 'MGW' ||
coin === 'MVP' ||
coin === 'KV' ||
coin === 'CEAL' ||
coin === 'MESH' ||
coin === 'WLC' ||
coin === 'MNZ' ||
coinUC === 'SUPERNET' ||
coinUC === 'REVS' ||
coinUC === 'SUPERNET' ||
coinUC === 'PANGEA' ||
coinUC === 'DEX' ||
coinUC === 'JUMBLR' ||
coinUC === 'BET' ||
coinUC === 'CRYPTO' ||
coinUC === 'COQUI' ||
coinUC === 'HODL' ||
coinUC === 'SHARK' ||
coinUC === 'BOTS' ||
coinUC === 'MGW' ||
coinUC === 'MVP' ||
coinUC === 'KV' ||
coinUC === 'CEAL' ||
coinUC === 'MESH' ||
coinUC === 'WLC' ||
coinUC === 'MNZ' ) {
return electrumJSNetworks . komodo ;
} else {
return electrumJSNetworks [ network ] ;
}
}
shepherd . seedToWif = function ( seed , network , iguana ) {
const bytes = sha256 ( seed , { asBytes : true } ) ;
if ( iguana ) {
bytes [ 0 ] &= 248 ;
bytes [ 31 ] &= 127 ;
bytes [ 31 ] |= 64 ;
}
function toHexString ( byteArray ) {
return Array . from ( byteArray , function ( byte ) {
return ( '0' + ( byte & 0xFF ) . toString ( 16 ) ) . slice ( - 2 ) ;
} ) . join ( '' ) ;
}
const hex = toHexString ( bytes ) ;
const key = new CoinKey ( new Buffer ( hex , 'hex' ) , {
private : shepherd . getNetworkData ( network ) . wif ,
public : shepherd . getNetworkData ( network ) . pubKeyHash ,
} ) ;
key . compressed = true ;
shepherd . log ( ` seedtowif priv key ${ key . privateWif } ` ) ;
shepherd . log ( ` seedtowif pub key ${ key . publicAddress } ` ) ;
return {
priv : key . privateWif ,
pub : key . publicAddress ,
} ;
}
shepherd . get ( '/electrum/seedtowif' , function ( req , res , next ) {
const keys = shepherd . seedToWif ( req . query . seed , req . query . network , req . query . iguana ) ;
const successObj = {
msg : 'success' ,
result : {
keys ,
} ,
} ;
res . end ( JSON . stringify ( successObj ) ) ;
} ) ;
shepherd . findNetworkObj = function ( coin ) {
for ( let key in electrumServers ) {
if ( electrumServers [ key ] . abbr === coin ) {
return key ;
}
}
}
shepherd . findCoinName = function ( network ) {
for ( let key in electrumServers ) {
if ( key === network ) {
return electrumServers [ key ] . abbr ;
}
}
}
shepherd . get ( '/electrum/servers/test' , function ( req , res , next ) {
const ecl = new electrumJSCore ( req . query . port , req . query . address , 'tcp' ) ; // tcp or tls
ecl . connect ( ) ;
ecl . serverVersion ( )
. then ( ( serverData ) => {
ecl . close ( ) ;
console . log ( 'serverData' ) ;
console . log ( serverData ) ;
if ( serverData &&
typeof serverData === 'string' &&
serverData . indexOf ( 'Electrum' ) > - 1 ) {
const successObj = {
msg : 'success' ,
result : true ,
} ;
res . end ( JSON . stringify ( successObj ) ) ;
} else {
const successObj = {
msg : 'error' ,
result : false ,
} ;
res . end ( JSON . stringify ( successObj ) ) ;
}
} ) ;
} ) ;
shepherd . get ( '/electrum/keys' , function ( req , res , next ) {
let _ matchingKeyPairs = 0 ;
let _ electrumKeys = { } ;
for ( let key in electrumServers ) {
const _ abbr = electrumServers [ key ] . abbr ;
const { priv , pub } = shepherd . seedToWif ( req . query . seed , shepherd . findNetworkObj ( _ abbr ) , req . query . iguana ) ;
if ( electrumKeys [ _ abbr ] . pub === pub &&
electrumKeys [ _ abbr ] . priv === priv ) {
_ matchingKeyPairs ++ ;
}
}
if ( req . query . active ) {
_ electrumKeys = JSON . parse ( JSON . stringify ( electrumKeys ) ) ;
for ( let key in _ electrumKeys ) {
if ( ! electrumCoins [ key ] ) {
delete _ electrumKeys [ key ] ;
}
}
} else {
_ electrumKeys = electrumKeys ;
}
shepherd . log ( JSON . stringify ( _ electrumKeys , null , '\t' ) ) ;
const successObj = {
msg : 'success' ,
result : _ matchingKeyPairs === Object . keys ( electrumKeys ) . length ? _ electrumKeys : false ,
} ;
res . end ( JSON . stringify ( successObj ) ) ;
} ) ;
shepherd . get ( '/electrum/login' , function ( req , res , next ) {
for ( let key in electrumServers ) {
const _ abbr = electrumServers [ key ] . abbr ;
const { priv , pub } = shepherd . seedToWif ( req . query . seed , shepherd . findNetworkObj ( _ abbr ) , req . query . iguana ) ;
electrumKeys [ _ abbr ] = {
priv ,
pub ,
} ;
}
electrumCoins . auth = true ;
shepherd . log ( JSON . stringify ( electrumKeys , null , '\t' ) ) ;
const successObj = {
msg : 'success' ,
result : 'true' ,
} ;
res . end ( JSON . stringify ( successObj ) ) ;
} ) ;
shepherd . get ( '/electrum/dev/logout' , function ( req , res , next ) {
electrumCoins . auth = false ;
electrumKeys = { } ;
const successObj = {
msg : 'success' ,
result : 'true' ,
} ;
res . end ( JSON . stringify ( successObj ) ) ;
} ) ;
shepherd . get ( '/electrum/bip39/seed' , function ( req , res , next ) {
// TODO
const bip39 = require ( 'bip39' ) ; // npm i -S bip39
const crypto = require ( 'crypto' ) ;
// what you describe as 'seed'
const randomBytes = crypto . randomBytes ( 16 ) ; // 128 bits is enough
// your 12 word phrase
const mnemonic = bip39 . entropyToMnemonic ( randomBytes . toString ( 'hex' ) ) ;
// what is accurately described as the wallet seed
// var seed = bip39.mnemonicToSeed(mnemonic) // you'll use this in #3 below
const seed = bip39 . mnemonicToSeed ( req . query . seed ) ;
console . log ( seed ) ;
const successObj = {
msg : 'success' ,
result : {
servers : electrumServers ,
} ,
} ;
res . end ( JSON . stringify ( successObj ) ) ;
console . log ( bitcoinJS . networks . komodo ) ;
const hdMaster = bitcoinJS . HDNode . fromSeedBuffer ( seed , electrumJSNetworks . komodo ) ; // seed from above
const key1 = hdMaster . derivePath ( 'm/0' ) ;
const key2 = hdMaster . derivePath ( 'm/1' ) ;
console . log ( hdMaster ) ;
console . log ( key1 . keyPair . toWIF ( ) ) ;
console . log ( key1 . keyPair . getAddress ( ) ) ;
console . log ( key2 . keyPair . toWIF ( ) ) ;
const hdnode = bitcoinJS . HDNode . fromSeedBuffer ( seed , electrumJSNetworks . komodo ) . deriveHardened ( 0 ) . derive ( 0 ) . derive ( 1 ) ;
console . log ( ` address: ${ hdnode . getAddress ( ) } ` ) ;
console . log ( ` priv (WIF): ${ hdnode . keyPair . toWIF ( ) } ` ) ;
} ) ;
// get merkle root
shepherd . getMerkleRoot = function ( txid , proof , pos ) {
const reverse = require ( 'buffer-reverse' ) ;
let hash = txid ;
let serialized ;
const _ sha256 = function ( data ) {
return crypto . createHash ( 'sha256' ) . update ( data ) . digest ( ) ;
}
console . log ( ` txid ${ txid } ` ) ;
console . log ( ` pos ${ pos } ` ) ;
console . log ( 'proof' ) ;
console . log ( proof ) ;
for ( i = 0 ; i < proof . length ; i ++ ) {
const _ hashBuff = new Buffer ( hash , 'hex' ) ;
const _ proofBuff = new Buffer ( proof [ i ] , 'hex' ) ;
if ( ( pos & 1 ) == 0 ) {
serialized = Buffer . concat ( [ reverse ( _ hashBuff ) , reverse ( _ proofBuff ) ] ) ;
} else {
serialized = Buffer . concat ( [ reverse ( _ proofBuff ) , reverse ( _ hashBuff ) ] ) ;
}
hash = reverse ( _ sha256 ( _ sha256 ( serialized ) ) ) . toString ( 'hex' ) ;
pos /= 2 ;
}
return hash ;
}
shepherd . verifyMerkle = function ( txid , height , serverList , mainServer ) {
// select random server
const getRandomIntInclusive = function ( min , max ) {
min = Math . ceil ( min ) ;
max = Math . floor ( max ) ;
return Math . floor ( Math . random ( ) * ( max - min + 1 ) ) + min ; // the maximum is inclusive and the minimum is inclusive
}
const _ rnd = getRandomIntInclusive ( 0 , serverList . length - 1 ) ;
const randomServer = serverList [ _ rnd ] ;
const _ randomServer = randomServer . split ( ':' ) ;
const _ mainServer = mainServer . split ( ':' ) ;
let ecl = new electrumJSCore ( _ mainServer [ 1 ] , _ mainServer [ 0 ] , 'tcp' ) ; // tcp or tls
return new Promise ( ( resolve , reject ) => {
console . log ( ` main server: ${ mainServer } ` ) ;
console . log ( ` verification server: ${ randomServer } ` ) ;
ecl . connect ( ) ;
ecl . blockchainTransactionGetMerkle ( txid , height )
. then ( ( merkleData ) => {
if ( merkleData &&
merkleData . merkle &&
merkleData . pos ) {
console . log ( 'electrum getmerkle =>' ) ;
console . log ( merkleData ) ;
ecl . close ( ) ;
const _ res = shepherd . getMerkleRoot ( txid , merkleData . merkle , merkleData . pos ) ;
console . log ( _ res ) ;
ecl = new electrumJSCore ( _ randomServer [ 1 ] , _ randomServer [ 0 ] , 'tcp' ) ;
ecl . connect ( ) ;
ecl . blockchainBlockGetHeader ( height )
. then ( ( blockInfo ) => {
if ( blockInfo &&
blockInfo [ 'merkle_root' ] ) {
ecl . close ( ) ;
console . log ( 'blockinfo =>' ) ;
console . log ( blockInfo ) ;
console . log ( blockInfo [ 'merkle_root' ] ) ;
if ( blockInfo &&
blockInfo [ 'merkle_root' ] ) {
if ( _ res === blockInfo [ 'merkle_root' ] ) {
resolve ( true ) ;
} else {
resolve ( false ) ;
}
} else {
resolve ( CONNECTION_ERROR_OR_INCOMPLETE_DATA ) ;
}
} else {
resolve ( CONNECTION_ERROR_OR_INCOMPLETE_DATA ) ;
}
} ) ;
} else {
resolve ( CONNECTION_ERROR_OR_INCOMPLETE_DATA ) ;
}
} ) ;
} ) ;
}
shepherd . verifyMerkleByCoin = function ( coin , txid , height ) {
const _ serverList = electrumCoins [ coin ] . serverList ;
console . log ( electrumCoins [ coin ] . server ) ;
console . log ( electrumCoins [ coin ] . serverList ) ;
return new Promise ( ( resolve , reject ) => {
if ( _ serverList !== 'none' ) {
let _ filteredServerList = [ ] ;
for ( let i = 0 ; i < _ serverList . length ; i ++ ) {
if ( _ serverList [ i ] !== electrumCoins [ coin ] . server . ip + ':' + electrumCoins [ coin ] . server . port ) {
_ filteredServerList . push ( _ serverList [ i ] ) ;
}
}
shepherd . verifyMerkle ( txid , height , _ filteredServerList , electrumCoins [ coin ] . server . ip + ':' + electrumCoins [ coin ] . server . port )
. then ( ( proof ) => {
resolve ( proof ) ;
} ) ;
} else {
resolve ( false ) ;
}
} ) ;
}
shepherd . get ( '/electrum/merkle/verify' , function ( req , res , next ) {
shepherd . verifyMerkleByCoin ( req . query . coin , req . query . txid , req . query . height )
. then ( ( verifyMerkleRes ) => {
const successObj = {
msg : 'success' ,
result : {
merkleProof : verifyMerkleRes ,
} ,
} ;
res . end ( JSON . stringify ( successObj ) ) ;
} ) ;
} ) ;
shepherd . get ( '/electrum/servers' , function ( req , res , next ) {
if ( req . query . abbr ) {
let _ electrumServers = { } ;
for ( let key in electrumServers ) {
_ electrumServers [ electrumServers [ key ] . abbr ] = electrumServers [ key ] ;
}
const successObj = {
msg : 'success' ,
result : {
servers : _ electrumServers ,
} ,
} ;
res . end ( JSON . stringify ( successObj ) ) ;
} else {
const successObj = {
msg : 'success' ,
result : {
servers : electrumServers ,
} ,
} ;
res . end ( JSON . stringify ( successObj ) ) ;
}
} ) ;
shepherd . get ( '/electrum/coins/server/set' , function ( req , res , next ) {
electrumCoins [ req . query . coin ] . server = {
ip : req . query . address ,
port : req . query . port ,
} ;
for ( let key in electrumServers ) {
if ( electrumServers [ key ] . abbr === req . query . coin ) { // a bit risky
electrumServers [ key ] . address = req . query . address ;
electrumServers [ key ] . port = req . query . port ;
break ;
}
}
console . log ( JSON . stringify ( electrumCoins [ req . query . coin ] ) , null , '\t' ) ;
const successObj = {
msg : 'success' ,
result : true ,
} ;
res . end ( JSON . stringify ( successObj ) ) ;
} ) ;
shepherd . addElectrumCoin = function ( coin ) {
for ( let key in electrumServers ) {
if ( electrumServers [ key ] . abbr === coin ) {
electrumCoins [ coin ] = {
name : key ,
abbr : coin ,
server : {
ip : electrumServers [ key ] . address ,
port : electrumServers [ key ] . port ,
} ,
serverList : electrumServers [ key ] . serverList ? electrumServers [ key ] . serverList : 'none' ,
txfee : 'calculated' /*electrumServers[key].txfee*/ ,
} ;
return true ;
}
}
}
shepherd . get ( '/electrum/coins/remove' , function ( req , res , next ) {
delete electrumCoins [ req . query . coin ] ;
const successObj = {
msg : 'success' ,
result ,
} ;
res . end ( JSON . stringify ( successObj ) ) ;
} ) ;
shepherd . get ( '/electrum/coins/add' , function ( req , res , next ) {
const result = shepherd . addElectrumCoin ( req . query . coin ) ;
const successObj = {
msg : 'success' ,
result ,
} ;
res . end ( JSON . stringify ( successObj ) ) ;
} ) ;
shepherd . get ( '/electrum/coins' , function ( req , res , next ) {
let _ electrumCoins = JSON . parse ( JSON . stringify ( electrumCoins ) ) ; // deep cloning
for ( let key in _ electrumCoins ) {
if ( electrumKeys [ key ] ) {
_ electrumCoins [ key ] . pub = electrumKeys [ key ] . pub ;
}
}
const successObj = {
msg : 'success' ,
result : _ electrumCoins ,
} ;
res . end ( JSON . stringify ( successObj ) ) ;
} ) ;
shepherd . kdmCalcInterest = function ( locktime , value ) { // value in sats
const timestampDiff = Math . floor ( Date . now ( ) / 1000 ) - locktime - 777 ;
const hoursPassed = Math . floor ( timestampDiff / 3600 ) ;
const minutesPassed = Math . floor ( ( timestampDiff - ( hoursPassed * 3600 ) ) / 60 ) ;
const secondsPassed = timestampDiff - ( hoursPassed * 3600 ) - ( minutesPassed * 60 ) ;
let timestampDiffMinutes = timestampDiff / 60 ;
let interest = 0 ;
console . log ( ` locktime ${ locktime } ` ) ;
console . log ( ` minutes converted ${ timestampDiffMinutes } ` ) ;
console . log ( ` passed ${ hoursPassed } h ${ minutesPassed } m ${ secondsPassed } s ` ) ;
// calc interest
if ( timestampDiffMinutes >= 60 ) {
if ( timestampDiffMinutes > 365 * 24 * 60 ) {
timestampDiffMinutes = 365 * 24 * 60 ;
}
timestampDiffMinutes -= 59 ;
console . log ( ` minutes if statement ${ timestampDiffMinutes } ` ) ;
// TODO: check if interest is > 5% yr
// calc ytd and 5% for 1 yr
// const hoursInOneYear = 365 * 24;
// const hoursDiff = hoursInOneYear - hoursPassed;
interest = ( ( Number ( value ) * 0.00000001 ) / 10512000 ) * timestampDiffMinutes ;
console . log ( ` interest ${ interest } ` ) ;
}
return interest ;
}
shepherd . get ( '/electrum/getbalance' , function ( req , res , next ) {
const network = req . query . network || shepherd . findNetworkObj ( req . query . coin ) ;
const ecl = new electrumJSCore ( electrumServers [ network ] . port , electrumServers [ network ] . address , electrumServers [ network ] . proto ) ; // tcp or tls
ecl . connect ( ) ;
ecl . blockchainAddressGetBalance ( req . query . address )
. then ( ( json ) => {
if ( json &&
json . hasOwnProperty ( 'confirmed' ) &&
json . hasOwnProperty ( 'unconfirmed' ) ) {
if ( network === 'komodo' ) {
ecl . connect ( ) ;
ecl . blockchainAddressListunspent ( req . query . address )
. then ( ( utxoList ) => {
if ( utxoList &&
utxoList . length ) {
// filter out < 10 KMD amounts
let _ utxo = [ ] ;
for ( let i = 0 ; i < utxoList . length ; i ++ ) {
console . log ( ` utxo ${ utxoList [ i ] [ 'tx_hash' ] } sats ${ utxoList [ i ] . value } value ${ Number ( utxoList [ i ] . value ) * 0.00000001 } ` ) ;
if ( Number ( utxoList [ i ] . value ) * 0.00000001 >= 10 ) {
_ utxo . push ( utxoList [ i ] ) ;
}
}
console . log ( 'filtered utxo list =>' ) ;
console . log ( _ utxo ) ;
if ( _ utxo &&
_ utxo . length ) {
let interestTotal = 0 ;
Promise . all ( _ utxo . map ( ( _ utxoItem , index ) => {
return new Promise ( ( resolve , reject ) => {
ecl . blockchainTransactionGet ( _ utxoItem [ 'tx_hash' ] )
. then ( ( _ rawtxJSON ) => {
console . log ( 'electrum gettransaction ==>' ) ;
console . log ( index + ' | ' + ( _ rawtxJSON . length - 1 ) ) ;
console . log ( _ rawtxJSON ) ;
// decode tx
const _ network = shepherd . getNetworkData ( network ) ;
const decodedTx = electrumJSTxDecoder ( _ rawtxJSON , _ network ) ;
if ( decodedTx &&
decodedTx . format &&
decodedTx . format . locktime > 0 ) {
interestTotal += shepherd . kdmCalcInterest ( decodedTx . format . locktime , _ utxoItem . value ) ;
}
console . log ( 'decoded tx =>' ) ;
console . log ( decodedTx ) ;
resolve ( true ) ;
} ) ;
} ) ;
} ) )
. then ( promiseResult => {
ecl . close ( ) ;
const successObj = {
msg : 'success' ,
result : {
balance : Number ( ( 0.00000001 * json . confirmed ) . toFixed ( 8 ) ) ,
unconfirmed : Number ( ( 0.00000001 * json . unconfirmed ) . toFixed ( 8 ) ) ,
unconfirmedSats : json . unconfirmed ,
balanceSats : json . confirmed ,
interest : Number ( interestTotal . toFixed ( 8 ) ) ,
interestSats : Math . floor ( interestTotal * 100000000 ) ,
total : interestTotal > 0 ? Number ( ( 0.00000001 * json . confirmed + interestTotal ) . toFixed ( 8 ) ) : 0 ,
totalSats : interestTotal > 0 ? json . confirmed + Math . floor ( interestTotal * 100000000 ) : 0 ,
} ,
} ;
res . end ( JSON . stringify ( successObj ) ) ;
} ) ;
} else {
const successObj = {
msg : 'success' ,
result : {
balance : Number ( ( 0.00000001 * json . confirmed ) . toFixed ( 8 ) ) ,
unconfirmed : Number ( ( 0.00000001 * json . unconfirmed ) . toFixed ( 8 ) ) ,
unconfirmedSats : json . unconfirmed ,
balanceSats : json . confirmed ,
interest : 0 ,
interestSats : 0 ,
total : 0 ,
totalSats : 0 ,
} ,
} ;
res . end ( JSON . stringify ( successObj ) ) ;
}
} else {
const successObj = {
msg : 'success' ,
result : {
balance : Number ( ( 0.00000001 * json . confirmed ) . toFixed ( 8 ) ) ,
unconfirmed : Number ( ( 0.00000001 * json . unconfirmed ) . toFixed ( 8 ) ) ,
unconfirmedSats : json . unconfirmed ,
balanceSats : json . confirmed ,
interest : 0 ,
interestSats : 0 ,
total : 0 ,
totalSats : 0 ,
} ,
} ;
res . end ( JSON . stringify ( successObj ) ) ;
}
} ) ;
} else {
ecl . close ( ) ;
console . log ( 'electrum getbalance ==>' ) ;
console . log ( json ) ;
const successObj = {
msg : 'success' ,
result : {
balance : Number ( ( 0.00000001 * json . confirmed ) . toFixed ( 8 ) ) ,
unconfirmed : Number ( ( 0.00000001 * json . unconfirmed ) . toFixed ( 8 ) ) ,
unconfirmedSats : json . unconfirmed ,
balanceSats : json . confirmed ,
} ,
} ;
res . end ( JSON . stringify ( successObj ) ) ;
}
} else {
const successObj = {
msg : 'error' ,
result : CONNECTION_ERROR_OR_INCOMPLETE_DATA ,
} ;
res . end ( JSON . stringify ( successObj ) ) ;
}
} ) ;
} ) ;
shepherd . sortTransactions = function ( transactions ) {
return transactions . sort ( function ( b , a ) {
if ( a . height < b . height ) {
return - 1 ;
}
if ( a . height > b . height ) {
return 1 ;
}
return 0 ;
} ) ;
}
shepherd . get ( '/electrum/listtransactions' , function ( req , res , next ) {
const network = req . query . network || shepherd . findNetworkObj ( req . query . coin ) ;
const ecl = new electrumJSCore ( electrumServers [ network ] . port , electrumServers [ network ] . address , electrumServers [ network ] . proto ) ; // tcp or tls
if ( ! req . query . full ) {
ecl . connect ( ) ;
ecl . blockchainAddressGetHistory ( req . query . address )
. then ( ( json ) => {
ecl . close ( ) ;
console . log ( 'electrum listtransactions ==>' ) ;
console . log ( json ) ;
json = shepherd . sortTransactions ( json ) ;
const successObj = {
msg : 'success' ,
result : {
listtransactions : json ,
} ,
} ;
res . end ( JSON . stringify ( successObj ) ) ;
} ) ;
} else {
// !expensive call!
// TODO: limit e.g. 1-10, 10-20 etc
const MAX_TX = req . query . maxlength || 10 ;
ecl . connect ( ) ;
ecl . blockchainNumblocksSubscribe ( )
. then ( ( currentHeight ) => {
if ( currentHeight &&
Number ( currentHeight ) > 0 ) {
ecl . blockchainAddressGetHistory ( req . query . address )
. then ( ( json ) => {
if ( json &&
json . length ) {
json = shepherd . sortTransactions ( json ) ;
json = json . slice ( 0 , MAX_TX ) ;
console . log ( json . length ) ;
let _ rawtx = [ ] ;
Promise . all ( json . map ( ( transaction , index ) => {
return new Promise ( ( resolve , reject ) => {
ecl . blockchainBlockGetHeader ( transaction . height )
. then ( ( blockInfo ) => {
if ( blockInfo &&
blockInfo . timestamp ) {
ecl . blockchainTransactionGet ( transaction [ 'tx_hash' ] )
. then ( ( _ rawtxJSON ) => {
console . log ( 'electrum gettransaction ==>' ) ;
console . log ( index + ' | ' + ( _ rawtxJSON . length - 1 ) ) ;
console . log ( _ rawtxJSON ) ;
// decode tx
const _ network = shepherd . getNetworkData ( network ) ;
const decodedTx = electrumJSTxDecoder ( _ rawtxJSON , _ network ) ;
let txInputs = [ ] ;
console . log ( 'decodedtx =>' ) ;
console . log ( decodedTx . outputs ) ;
if ( decodedTx &&
decodedTx . inputs ) {
Promise . all ( decodedTx . inputs . map ( ( _ decodedInput , index ) => {
return new Promise ( ( _ resolve , _ reject ) => {
if ( _ decodedInput . txid !== '0000000000000000000000000000000000000000000000000000000000000000' ) {
ecl . blockchainTransactionGet ( _ decodedInput . txid )
. then ( ( rawInput ) => {
console . log ( 'electrum raw input tx ==>' ) ;
const decodedVinVout = electrumJSTxDecoder ( rawInput , _ network ) ;
if ( decodedVinVout ) {
console . log ( decodedVinVout . outputs [ _ decodedInput . n ] ) ;
txInputs . push ( decodedVinVout . outputs [ _ decodedInput . n ] ) ;
_ resolve ( true ) ;
} else {
_ resolve ( true ) ;
}
} ) ;
} else {
_ resolve ( true ) ;
}
} ) ;
} ) )
. then ( promiseResult => {
const _ parsedTx = {
network : decodedTx . network ,
format : decodedTx . format ,
inputs : txInputs ,
outputs : decodedTx . outputs ,
height : transaction . height ,
timestamp : blockInfo . timestamp ,
confirmations : currentHeight - transaction . height ,
} ;
const formattedTx = shepherd . parseTransactionAddresses ( _ parsedTx , req . query . address , network ) ;
if ( formattedTx . type ) {
_ rawtx . push ( formattedTx ) ;
} else {
_ rawtx . push ( formattedTx [ 0 ] ) ;
_ rawtx . push ( formattedTx [ 1 ] ) ;
}
resolve ( true ) ;
} ) ;
} else {
const _ parsedTx = {
network : decodedTx . network ,
format : 'cant parse' ,
inputs : 'cant parse' ,
outputs : 'cant parse' ,
height : transaction . height ,
timestamp : blockInfo . timestamp ,
confirmations : currentHeight - transaction . height ,
} ;
const formattedTx = shepherd . parseTransactionAddresses ( _ parsedTx , req . query . address , network ) ;
_ rawtx . push ( formattedTx ) ;
resolve ( true ) ;
}
} ) ;
} else {
const _ parsedTx = {
network : 'cant parse' ,
format : 'cant parse' ,
inputs : 'cant parse' ,
outputs : 'cant parse' ,
height : transaction . height ,
timestamp : 'cant get block info' ,
confirmations : currentHeight - transaction . height ,
} ;
const formattedTx = shepherd . parseTransactionAddresses ( _ parsedTx , req . query . address , network ) ;
_ rawtx . push ( formattedTx ) ;
resolve ( true ) ;
}
} ) ;
} ) ;
} ) )
. then ( promiseResult => {
ecl . close ( ) ;
const successObj = {
msg : 'success' ,
result : _ rawtx ,
} ;
res . end ( JSON . stringify ( successObj ) ) ;
} ) ;
} else {
const successObj = {
msg : 'success' ,
result : [ ] ,
} ;
res . end ( JSON . stringify ( successObj ) ) ;
}
} ) ;
} else {
const successObj = {
msg : 'error' ,
result : 'cant get current height' ,
} ;
res . end ( JSON . stringify ( successObj ) ) ;
}
} ) ;
}
} ) ;
shepherd . get ( '/electrum/gettransaction' , function ( req , res , next ) {
const ecl = new electrumJSCore ( electrumServers [ req . query . network ] . port , electrumServers [ req . query . network ] . address , electrumServers [ req . query . network ] . proto ) ; // tcp or tls
ecl . connect ( ) ;
ecl . blockchainTransactionGet ( req . query . txid )
. then ( ( json ) => {
ecl . close ( ) ;
console . log ( 'electrum gettransaction ==>' ) ;
console . log ( json ) ;
const successObj = {
msg : 'success' ,
result : {
gettransaction : json ,
} ,
} ;
res . end ( JSON . stringify ( successObj ) ) ;
} ) ;
} ) ;
shepherd . parseTransactionAddresses = function ( tx , targetAddress , network ) {
// TODO: - sum vins / sum vouts to the same address
// - multi vin multi vout
// - detect change address
let result = [ ] ;
let _ parse = {
inputs : { } ,
outputs : { } ,
} ;
let _ sum = {
inputs : 0 ,
outputs : 0 ,
} ;
let _ total = {
inputs : 0 ,
outputs : 0 ,
} ;
if ( tx . format === 'cant parse' ) {
return {
type : 'unknown' ,
amount : 'unknown' ,
address : targetAddress ,
timestamp : tx . timestamp ,
txid : tx . format . txid ,
confirmations : tx . confirmations ,
}
}
for ( let key in _ parse ) {
if ( ! tx [ key ] . length ) {
_ parse [ key ] = [ ] ;
_ parse [ key ] . push ( tx [ key ] ) ;
} else {
_ parse [ key ] = tx [ key ] ;
}
for ( let i = 0 ; i < _ parse [ key ] . length ; i ++ ) {
console . log ( key + ' ==>' ) ;
console . log ( _ parse [ key ] [ i ] ) ;
console . log ( Number ( _ parse [ key ] [ i ] . value ) ) ;
_ total [ key ] += Number ( _ parse [ key ] [ i ] . value ) ;
if ( _ parse [ key ] [ i ] . scriptPubKey &&
_ parse [ key ] [ i ] . scriptPubKey . addresses &&
_ parse [ key ] [ i ] . scriptPubKey . addresses [ 0 ] === targetAddress &&
_ parse [ key ] [ i ] . value ) {
_ sum [ key ] += Number ( _ parse [ key ] [ i ] . value ) ;
}
}
}
if ( _ sum . inputs > 0 &&
_ sum . outputs > 0 ) {
// vin + change, break into two tx
result = [ { // reorder since tx sort by default is from newest to oldest
type : 'sent' ,
amount : Number ( _ sum . inputs . toFixed ( 8 ) ) ,
address : targetAddress ,
timestamp : tx . timestamp ,
txid : tx . format . txid ,
confirmations : tx . confirmations ,
} , {
type : 'received' ,
amount : Number ( _ sum . outputs . toFixed ( 8 ) ) ,
address : targetAddress ,
timestamp : tx . timestamp ,
txid : tx . format . txid ,
confirmations : tx . confirmations ,
} ] ;
if ( network === 'komodo' ) { // calc claimed interest amount
const vinVoutDiff = _ total . inputs - _ total . outputs ;
if ( vinVoutDiff < 0 ) {
result [ 1 ] . interest = Number ( vinVoutDiff . toFixed ( 8 ) ) ;
}
}
} else if ( _ sum . inputs === 0 && _ sum . outputs > 0 ) {
result = {
type : 'received' ,
amount : Number ( _ sum . outputs . toFixed ( 8 ) ) ,
address : targetAddress ,
timestamp : tx . timestamp ,
txid : tx . format . txid ,
confirmations : tx . confirmations ,
} ;
} else if ( _ sum . inputs > 0 && _ sum . outputs === 0 ) {
result = {
type : 'sent' ,
amount : Number ( _ sum . inputs . toFixed ( 8 ) ) ,
address : targetAddress ,
timestamp : tx . timestamp ,
txid : tx . format . txid ,
confirmations : tx . confirmations ,
} ;
} else {
// (?)
result = {
type : 'other' ,
amount : 'unknown' ,
address : targetAddress ,
timestamp : tx . timestamp ,
txid : tx . format . txid ,
confirmations : tx . confirmations ,
} ;
}
console . log ( 'parseTransactionAddresses result ==>' ) ;
console . log ( _ sum ) ;
console . log ( result ) ;
return result ;
}
shepherd . get ( '/electrum/getblockinfo' , function ( req , res , next ) {
shepherd . electrumGetBlockInfo ( req . query . height , req . query . network )
. then ( function ( json ) {
const successObj = {
msg : 'success' ,
result : {
getblockinfo : json ,
} ,
} ;
res . end ( JSON . stringify ( successObj ) ) ;
} ) ;
} ) ;
shepherd . electrumGetBlockInfo = function ( height , network ) {
return new Promise ( ( resolve , reject ) => {
const ecl = new electrumJSCore ( electrumServers [ network ] . port , electrumServers [ network ] . address , electrumServers [ network ] . proto ) ; // tcp or tls
ecl . connect ( ) ;
ecl . blockchainBlockGetHeader ( height )
. then ( ( json ) => {
ecl . close ( ) ;
console . log ( 'electrum getblockinfo ==>' ) ;
console . log ( json ) ;
resolve ( json ) ;
} ) ;
} ) ;
}
shepherd . get ( '/electrum/getcurrentblock' , function ( req , res , next ) {
shepherd . electrumGetCurrentBlock ( req . query . network )
. then ( function ( json ) {
const successObj = {
msg : 'success' ,
result : {
getcurrentblock : json ,
} ,
} ;
res . end ( JSON . stringify ( successObj ) ) ;
} ) ;
} ) ;
shepherd . electrumGetCurrentBlock = function ( network ) {
return new Promise ( ( resolve , reject ) => {
const ecl = new electrumJSCore ( electrumServers [ network ] . port , electrumServers [ network ] . address , electrumServers [ network ] . proto ) ; // tcp or tls
ecl . connect ( ) ;
ecl . blockchainNumblocksSubscribe ( )
. then ( ( json ) => {
ecl . close ( ) ;
console . log ( 'electrum currentblock ==>' ) ;
console . log ( json ) ;
resolve ( json ) ;
} ) ;
} ) ;
}
shepherd . get ( '/electrum/decoderawtx' , function ( req , res , next ) {
const _ network = shepherd . getNetworkData ( req . query . network ) ;
const _ rawtx = req . query . rawtx ;
// const _rawtx = '0100000001dd6d064f5665f8454293ecaa9dbb55accf4f7e443d35f3b5ab7760f54b6c15fe000000006a473044022056355585a4a501ec9afc96aa5df124cf29ad3ac6454b47cd07cd7d89ec95ec2b022074c4604ee349d30e5336f210598e4dc576bf16ebeb67eeac3f4e82f56e930fee012103b90ba01af308757054e0484bb578765d5df59c4a57adbb94e2419df5e7232a63feffffff0289fc923b000000001976a91424af38fcb13bbc171b0b42bb017244a53b6bb2fa88ac20a10700000000001976a9142f4c0f91fc06ac228c120aee41741d0d3909683288ac49258b58';
const decodedTx = electrumJSTxDecoder ( _ rawtx , _ network ) ;
if ( req . query . parseonly ||
decodedTx . inputs [ 0 ] . txid === '0000000000000000000000000000000000000000000000000000000000000000' ) {
const successObj = {
msg : 'success' ,
result : {
decodedTx : {
network : decodedTx . network ,
format : decodedTx . format ,
inputs : decodedTx . inputs ,
outputs : decodedTx . outputs ,
} ,
} ,
} ;
res . end ( JSON . stringify ( successObj ) ) ;
} else {
const ecl = new electrumJSCore ( electrumServers [ req . query . network ] . port , electrumServers [ req . query . network ] . address , electrumServers [ req . query . network ] . proto ) ; // tcp or tls
ecl . connect ( ) ;
ecl . blockchainTransactionGet ( decodedTx . inputs [ 0 ] . txid )
. then ( ( json ) => {
ecl . close ( ) ;
console . log ( 'electrum decoderawtx input tx ==>' ) ;
console . log ( json ) ;
const decodedVin = electrumJSTxDecoder ( json , _ network ) ;
const successObj = {
msg : 'success' ,
result : {
decodedTx : {
network : decodedTx . network ,
format : decodedTx . format ,
inputs : decodedVin . outputs [ decodedTx . inputs [ 0 ] . n ] ,
outputs : decodedTx . outputs ,
} ,
} ,
} ;
res . end ( JSON . stringify ( successObj ) ) ;
} ) ;
}
} ) ;
// deprecated, remove
shepherd . findUtxoSet = function ( utxoList , target ) {
let result = [ ] ;
let sum = 0 ;
function findUtxoSubset ( ) {
if ( Number ( utxoList [ 0 ] . value ) >= Number ( target ) ) {
sum = utxoList [ 0 ] . value ;
result . push ( utxoList [ 0 ] ) ;
} else {
for ( let i = 0 ; i < utxoList . length ; i ++ ) {
sum += Number ( utxoList [ i ] . value ) ;
result . push ( utxoList [ i ] ) ;
if ( sum >= Number ( target ) ) {
break ;
}
}
}
}
// search time
const startTime = process . hrtime ( ) ;
const res = findUtxoSubset ( ) ;
const diff = process . hrtime ( startTime ) ;
const change = sum - target ;
// console.log('utxo set search result: ', result);
console . log ( 'target: ' + target ) ;
console . log ( 'total utxos: ' + utxoList . length ) ;
console . log ( 'utxo sum: ' + sum ) ;
console . log ( 'change: ' + change ) ;
console . log ( ` Time: ${ ( diff [ 0 ] * 1e9 + diff [ 1 ] ) / 1000000 } ms ` ) ;
return {
set : result ,
change
} ;
}
shepherd . get ( '/electrum/subset' , function ( req , res , next ) {
const _ utxoSet = shepherd . findUtxoSet ( null , Number ( req . query . target ) + Number ( electrumServers [ req . query . network ] . txfee ) ) ; // target + txfee
const successObj = {
msg : 'success' ,
result : {
utxoSet : _ utxoSet . set ,
change : _ utxoSet . change ,
} ,
} ;
res . end ( JSON . stringify ( successObj ) ) ;
} ) ;
// single sig
shepherd . buildSignedTx = function ( sendTo , changeAddress , wif , network , utxo , changeValue , spendValue ) {
let key = bitcoinJS . ECPair . fromWIF ( wif , shepherd . getNetworkData ( network ) ) ;
let tx = new bitcoinJS . TransactionBuilder ( shepherd . getNetworkData ( network ) ) ;
// console.log(`buildSignedTx priv key ${wif}`);
console . log ( ` buildSignedTx pub key ${ key . getAddress ( ) . toString ( ) } ` ) ;
// console.log('buildSignedTx std tx fee ' + electrumServers[network].txfee);
for ( let i = 0 ; i < utxo . length ; i ++ ) {
tx . addInput ( utxo [ i ] . txid , utxo [ i ] . vout ) ;
}
tx . addOutput ( sendTo , Number ( spendValue ) ) ;
if ( changeValue > 0 ) {
tx . addOutput ( changeAddress , Number ( changeValue ) ) ;
}
if ( network === 'komodo' ||
network === 'KMD' ) {
const _ locktime = Math . floor ( Date . now ( ) / 1000 ) - 777 ;
tx . setLockTime ( _ locktime ) ;
console . log ( ` kmd tx locktime set to ${ _ locktime } ` ) ;
}
console . log ( 'buildSignedTx unsigned tx data vin' ) ;
console . log ( tx . tx . ins ) ;
console . log ( 'buildSignedTx unsigned tx data vout' ) ;
console . log ( tx . tx . outs ) ;
console . log ( 'buildSignedTx unsigned tx data' ) ;
console . log ( tx ) ;
for ( let i = 0 ; i < utxo . length ; i ++ ) {
tx . sign ( i , key ) ;
}
const rawtx = tx . build ( ) . toHex ( ) ;
console . log ( 'buildSignedTx signed tx hex' ) ;
console . log ( rawtx ) ;
return rawtx ;
}
shepherd . maxSpendBalance = function ( utxoList , fee ) {
let maxSpendBalance = 0 ;
for ( let i = 0 ; i < utxoList . length ; i ++ ) {
maxSpendBalance += Number ( utxoList [ i ] . value ) ;
}
if ( fee ) {
return Number ( maxSpendBalance ) - Number ( fee ) ;
} else {
return maxSpendBalance ;
}
}
shepherd . get ( '/electrum/createrawtx' , function ( req , res , next ) {
// txid 64 char
const network = req . query . network || shepherd . findNetworkObj ( req . query . coin ) ;
const ecl = new electrumJSCore ( electrumServers [ network ] . port , electrumServers [ network ] . address , electrumServers [ network ] . proto ) ; // tcp or tls
const outputAddress = req . query . address ;
const changeAddress = req . query . change ;
let wif = req . query . wif ;
const value = Number ( req . query . value ) ;
const push = req . query . push ;
const fee = electrumServers [ network ] . txfee ;
if ( req . query . gui ) {
wif = electrumKeys [ req . query . coin ] . priv ;
}
ecl . connect ( ) ;
shepherd . listunspent ( ecl , changeAddress , network , true , true )
. then ( ( utxoList ) => {
ecl . close ( ) ;
if ( utxoList &&
utxoList . length ) {
let utxoListFormatted = [ ] ;
let totalInterest = 0 ;
let totalInterestUTXOCount = 0 ;
let interestClaimThreshold = 200 ;
let utxoVerified = true ;
for ( let i = 0 ; i < utxoList . length ; i ++ ) {
if ( network === 'komodo' ) {
utxoListFormatted . push ( {
txid : utxoList [ i ] . txid ,
vout : utxoList [ i ] . vout ,
value : Number ( utxoList [ i ] . amountSats ) ,
interestSats : Number ( utxoList [ i ] . interestSats ) ,
verified : utxoList [ i ] . verified ? utxoList [ i ] . verified : false ,
} ) ;
} else {
utxoListFormatted . push ( {
txid : utxoList [ i ] . txid ,
vout : utxoList [ i ] . vout ,
value : Number ( utxoList [ i ] . amountSats ) ,
verified : utxoList [ i ] . verified ? utxoList [ i ] . verified : false ,
} ) ;
}
}
console . log ( 'electrum listunspent unformatted ==>' ) ;
console . log ( utxoList ) ;
console . log ( 'electrum listunspent formatted ==>' ) ;
console . log ( utxoListFormatted ) ;
const _ maxSpendBalance = Number ( shepherd . maxSpendBalance ( utxoListFormatted ) ) ;
let targets = [ {
address : outputAddress ,
value : value > _ maxSpendBalance ? _ maxSpendBalance : value ,
} ] ;
console . log ( 'targets =>' ) ;
console . log ( targets ) ;
const feeRate = 20 ; // sats/byte
// default coin selection algo blackjack with fallback to accumulative
// make a first run, calc approx tx fee
// if ins and outs are empty reduce max spend by txfee
let { inputs , outputs , fee } = coinSelect ( utxoListFormatted , targets , feeRate ) ;
console . log ( 'coinselect res =>' ) ;
console . log ( 'coinselect inputs =>' ) ;
console . log ( inputs ) ;
console . log ( 'coinselect outputs =>' ) ;
console . log ( outputs ) ;
console . log ( 'coinselect calculated fee =>' ) ;
console . log ( fee ) ;
if ( ! inputs &&
! outputs ) {
targets [ 0 ] . value = targets [ 0 ] . value - electrumServers [ network ] . txfee ;
console . log ( 'second run' ) ;
console . log ( 'coinselect adjusted targets =>' ) ;
console . log ( targets ) ;
const secondRun = coinSelect ( utxoListFormatted , targets , feeRate ) ;
inputs = secondRun . inputs ;
outputs = secondRun . outputs ;
fee = secondRun . fee ;
console . log ( 'coinselect inputs =>' ) ;
console . log ( inputs ) ;
console . log ( 'coinselect outputs =>' ) ;
console . log ( outputs ) ;
console . log ( 'coinselect fee =>' ) ;
console . log ( fee ) ;
}
let _ change = 0 ;
if ( outputs &&
outputs . length === 2 ) {
_ change = outputs [ 1 ] . value ;
}
// check if any outputs are unverified
if ( inputs &&
inputs . length ) {
for ( let i = 0 ; i < inputs . length ; i ++ ) {
if ( ! inputs [ i ] . verified ) {
utxoVerified = false ;
break ;
}
}
for ( let i = 0 ; i < inputs . length ; i ++ ) {
if ( Number ( inputs [ i ] . interestSats ) > interestClaimThreshold ) {
totalInterest += Number ( inputs [ i ] . interestSats ) ;
totalInterestUTXOCount ++ ;
}
}
}
const _ maxSpend = shepherd . maxSpendBalance ( utxoListFormatted ) ;
if ( value > _ maxSpend ) {
const successObj = {
msg : 'error' ,
result : ` Spend value is too large. Max available amount is ${ Number ( ( _ maxSpend * 0.00000001 . toFixed ( 8 ) ) ) } ` ,
} ;
res . end ( JSON . stringify ( successObj ) ) ;
} else {
console . log ( ` maxspend ${ _ maxSpend } ( ${ _ maxSpend * 0.00000001 } ) ` ) ;
console . log ( ` value ${ value } ` ) ;
console . log ( ` sendto ${ outputAddress } amount ${ value } ( ${ value * 0.00000001 } ) ` ) ;
console . log ( ` changeto ${ changeAddress } amount ${ _ change } ( ${ _ change * 0.00000001 } ) ` ) ;
// account for KMD interest
if ( network === 'komodo' &&
totalInterest > 0 ) {
// account for extra vout
const _ feeOverhead = outputs . length === 1 ? shepherd . estimateTxSize ( 0 , 1 ) * feeRate : 0 ;
console . log ( ` max interest to claim ${ totalInterest } ( ${ totalInterest * 0.00000001 } ) ` ) ;
console . log ( 'estimated fee overhead ' + _ feeOverhead ) ;
console . log ( ` current change amount ${ _ change } ( ${ _ change * 0.00000001 } ), boosted change amount ${ _ change + ( totalInterest - _ feeOverhead ) } ( ${ ( _ change + ( totalInterest - _ feeOverhead ) ) * 0.00000001 } ) ` ) ;
_ change = _ change + ( totalInterest - _ feeOverhead ) ;
}
if ( ! inputs &&
! outputs ) {
const successObj = {
msg : 'error' ,
result : 'Can\'t find best fit utxo. Try lower amount.' ,
} ;
res . end ( JSON . stringify ( successObj ) ) ;
} else {
let vinSum = 0 ;
for ( let i = 0 ; i < inputs . length ; i ++ ) {
vinSum += inputs [ i ] . value ;
}
console . log ( ` vin sum ${ vinSum } ( ${ vinSum * 0.00000001 } ) ` ) ;
const _ estimatedFee = vinSum - outputs [ 0 ] . value - _ change ;
console . log ( ` estimatedFee ${ _ estimatedFee } ( ${ _ estimatedFee * 0.00000001 } ) ` ) ;
const _ rawtx = shepherd . buildSignedTx ( outputAddress , changeAddress , wif , network , inputs , _ change , value ) ;
if ( ! push ||
push === 'false' ) {
const successObj = {
msg : 'success' ,
result : {
utxoSet : inputs ,
change : _ change ,
// wif,
fee ,
value ,
outputAddress ,
changeAddress ,
network ,
rawtx : _ rawtx ,
utxoVerified ,
} ,
} ;
res . end ( JSON . stringify ( successObj ) ) ;
} else {
const ecl = new electrumJSCore ( electrumServers [ network ] . port , electrumServers [ network ] . address , electrumServers [ network ] . proto ) ; // tcp or tls
ecl . connect ( ) ;
ecl . blockchainTransactionBroadcast ( _ rawtx )
. then ( ( txid ) => {
ecl . close ( ) ;
if ( txid &&
txid . indexOf ( 'bad-txns-inputs-spent' ) > - 1 ) {
const successObj = {
msg : 'error' ,
result : 'Bad transaction inputs spent' ,
raw : {
utxoSet : inputs ,
change : _ change ,
fee ,
value ,
outputAddress ,
changeAddress ,
network ,
rawtx : _ rawtx ,
txid ,
utxoVerified ,
} ,
} ;
res . end ( JSON . stringify ( successObj ) ) ;
} else {
if ( txid &&
txid . length === 64 ) {
if ( txid . indexOf ( 'bad-txns-in-belowout' ) > - 1 ) {
const successObj = {
msg : 'error' ,
result : 'Bad transaction inputs spent' ,
raw : {
utxoSet : inputs ,
change : _ change ,
fee ,
value ,
outputAddress ,
changeAddress ,
network ,
rawtx : _ rawtx ,
txid ,
utxoVerified ,
} ,
} ;
res . end ( JSON . stringify ( successObj ) ) ;
} else {
const successObj = {
msg : 'success' ,
result : {
utxoSet : inputs ,
change : _ change ,
fee ,
// wif,
value ,
outputAddress ,
changeAddress ,
network ,
rawtx : _ rawtx ,
txid ,
utxoVerified ,
} ,
} ;
res . end ( JSON . stringify ( successObj ) ) ;
}
} else {
if ( txid &&
txid . indexOf ( 'bad-txns-in-belowout' ) > - 1 ) {
const successObj = {
msg : 'error' ,
result : 'Bad transaction inputs spent' ,
raw : {
utxoSet : inputs ,
change : _ change ,
fee ,
value ,
outputAddress ,
changeAddress ,
network ,
rawtx : _ rawtx ,
txid ,
utxoVerified ,
} ,
} ;
res . end ( JSON . stringify ( successObj ) ) ;
} else {
const successObj = {
msg : 'error' ,
result : 'Can\'t broadcast transaction' ,
raw : {
utxoSet : inputs ,
change : _ change ,
fee ,
value ,
outputAddress ,
changeAddress ,
network ,
rawtx : _ rawtx ,
txid ,
utxoVerified ,
} ,
} ;
res . end ( JSON . stringify ( successObj ) ) ;
}
}
}
} ) ;
}
}
}
} else {
const successObj = {
msg : 'error' ,
result : utxoList ,
} ;
res . end ( JSON . stringify ( successObj ) ) ;
}
} ) ;
} ) ;
shepherd . get ( '/electrum/pushtx' , function ( req , res , next ) {
const rawtx = req . query . rawtx ;
const ecl = new electrumJSCore ( electrumServers [ req . query . network ] . port , electrumServers [ req . query . network ] . address , electrumServers [ req . query . network ] . proto ) ; // tcp or tls
ecl . connect ( ) ;
ecl . blockchainTransactionBroadcast ( rawtx )
. then ( ( json ) => {
ecl . close ( ) ;
console . log ( 'electrum pushtx ==>' ) ;
console . log ( json ) ;
const successObj = {
msg : 'success' ,
result : {
txid : json ,
} ,
} ;
res . end ( JSON . stringify ( successObj ) ) ;
} ) ;
} ) ;
shepherd . listunspent = function ( ecl , address , network , full , verify ) {
let _ atLeastOneDecodeTxFailed = false ;
if ( full ) {
return new Promise ( function ( resolve , reject ) {
ecl . connect ( ) ;
ecl . blockchainAddressListunspent ( address )
. then ( ( _ utxoJSON ) => {
if ( _ utxoJSON &&
_ utxoJSON . length ) {
let formattedUtxoList = [ ] ;
let _ utxo = [ ] ;
ecl . blockchainNumblocksSubscribe ( )
. then ( ( currentHeight ) => {
if ( currentHeight &&
Number ( currentHeight ) > 0 ) {
// filter out unconfirmed utxos
for ( let i = 0 ; i < _ utxoJSON . length ; i ++ ) {
if ( Number ( currentHeight ) - Number ( _ utxoJSON [ i ] . height ) !== 0 ) {
_ utxo . push ( _ utxoJSON [ i ] ) ;
}
}
if ( ! _ utxo . length ) { // no confirmed utxo
resolve ( 'no valid utxo' ) ;
} else {
Promise . all ( _ utxo . map ( ( _ utxoItem , index ) => {
return new Promise ( ( resolve , reject ) => {
ecl . blockchainTransactionGet ( _ utxoItem [ 'tx_hash' ] )
. then ( ( _ rawtxJSON ) => {
console . log ( 'electrum gettransaction ==>' ) ;
console . log ( index + ' | ' + ( _ rawtxJSON . length - 1 ) ) ;
console . log ( _ rawtxJSON ) ;
// decode tx
const _ network = shepherd . getNetworkData ( network ) ;
const decodedTx = electrumJSTxDecoder ( _ rawtxJSON , _ network ) ;
console . log ( 'decoded tx =>' ) ;
console . log ( decodedTx ) ;
if ( ! decodedTx ) {
_ atLeastOneDecodeTxFailed = true ;
resolve ( 'cant decode tx' ) ;
} else {
if ( network === 'komodo' ) {
let interest = 0 ;
if ( Number ( _ utxoItem . value ) * 0.00000001 >= 10 &&
decodedTx . format . locktime > 0 ) {
interest = shepherd . kdmCalcInterest ( decodedTx . format . locktime , _ utxoItem . value ) ;
}
let _ resolveObj = {
txid : _ utxoItem [ 'tx_hash' ] ,
vout : _ utxoItem [ 'tx_pos' ] ,
address ,
amount : Number ( _ utxoItem . value ) * 0.00000001 ,
amountSats : _ utxoItem . value ,
interest : interest ,
interestSats : Math . floor ( interest * 100000000 ) ,
confirmations : currentHeight - _ utxoItem . height ,
spendable : true ,
verified : false ,
} ;
// merkle root verification agains another electrum server
if ( verify ) {
shepherd . verifyMerkleByCoin ( shepherd . findCoinName ( network ) , _ utxoItem [ 'tx_hash' ] , _ utxoItem . height )
. then ( ( verifyMerkleRes ) => {
if ( verifyMerkleRes && verifyMerkleRes === CONNECTION_ERROR_OR_INCOMPLETE_DATA ) {
verifyMerkleRes = false ;
}
_ resolveObj . verified = verifyMerkleRes ;
resolve ( _ resolveObj ) ;
} ) ;
} else {
resolve ( _ resolveObj ) ;
}
} else {
let _ resolveObj = {
txid : _ utxoItem [ 'tx_hash' ] ,
vout : _ utxoItem [ 'tx_pos' ] ,
address ,
amount : Number ( _ utxoItem . value ) * 0.00000001 ,
amountSats : _ utxoItem . value ,
confirmations : currentHeight - _ utxoItem . height ,
spendable : true ,
verified : false ,
} ;
// merkle root verification agains another electrum server
if ( verify ) {
shepherd . verifyMerkleByCoin ( shepherd . findCoinName ( network ) , _ utxoItem [ 'tx_hash' ] , _ utxoItem . height )
. then ( ( verifyMerkleRes ) => {
if ( verifyMerkleRes &&
verifyMerkleRes === CONNECTION_ERROR_OR_INCOMPLETE_DATA ) {
verifyMerkleRes = false ;
}
_ resolveObj . verified = verifyMerkleRes ;
resolve ( _ resolveObj ) ;
} ) ;
} else {
resolve ( _ resolveObj ) ;
}
}
}
} ) ;
} ) ;
} ) )
. then ( promiseResult => {
ecl . close ( ) ;
if ( ! _ atLeastOneDecodeTxFailed ) {
console . log ( promiseResult ) ;
resolve ( promiseResult ) ;
} else {
console . log ( 'listunspent error, cant decode tx(s)' ) ;
resolve ( 'decode error' ) ;
}
} ) ;
}
} else {
resolve ( 'cant get current height' ) ;
}
} ) ;
} else {
ecl . close ( ) ;
resolve ( CONNECTION_ERROR_OR_INCOMPLETE_DATA ) ;
}
} ) ;
} ) ;
} else {
return new Promise ( function ( resolve , reject ) {
ecl . connect ( ) ;
ecl . blockchainAddressListunspent ( address )
. then ( ( json ) => {
ecl . close ( ) ;
if ( json &&
json . length ) {
resolve ( json ) ;
} else {
resolve ( CONNECTION_ERROR_OR_INCOMPLETE_DATA ) ;
}
} ) ;
} ) ;
}
}
shepherd . get ( '/electrum/listunspent' , function ( req , res , next ) {
const network = req . query . network || shepherd . findNetworkObj ( req . query . coin ) ;
const ecl = new electrumJSCore ( electrumServers [ network ] . port , electrumServers [ network ] . address , electrumServers [ network ] . proto ) ; // tcp or tls
if ( req . query . full &&
req . query . full === 'true' ) {
shepherd . listunspent ( ecl , req . query . address , network , true , req . query . verify )
. then ( ( listunspent ) => {
console . log ( 'electrum listunspent ==>' ) ;
const successObj = {
msg : 'success' ,
result : listunspent ,
} ;
res . end ( JSON . stringify ( successObj ) ) ;
} ) ;
} else {
shepherd . listunspent ( ecl , req . query . address , network )
. then ( ( listunspent ) => {
ecl . close ( ) ;
console . log ( 'electrum listunspent ==>' ) ;
const successObj = {
msg : 'success' ,
result : listunspent ,
} ;
res . end ( JSON . stringify ( successObj ) ) ;
} ) ;
}
} ) ;
shepherd . get ( '/electrum/estimatefee' , function ( req , res , next ) {
const ecl = new electrumJSCore ( electrumServers [ req . query . network ] . port , electrumServers [ req . query . network ] . address , electrumServers [ req . query . network ] . proto ) ; // tcp or tls
ecl . connect ( ) ;
ecl . blockchainEstimatefee ( req . query . blocks )
. then ( ( json ) => {
ecl . close ( ) ;
console . log ( 'electrum estimatefee ==>' ) ;
const successObj = {
msg : 'success' ,
result : {
estimatefee : json ,
} ,
} ;
res . end ( JSON . stringify ( successObj ) ) ;
} ) ;
} ) ;
shepherd . estimateTxSize = function ( numVins , numOuts ) {
// in x 180 + out x 34 + 10 plus or minus in
return numVins * 180 + numOuts * 34 + 11 ;
}
/ *
* list native coind
* type :
* params :
* /
shepherd . get ( '/coind/list' , function ( req , res , next ) {
const successObj = {
msg : 'success' ,
result : shepherd . nativeCoindList ,
} ;
res . end ( JSON . stringify ( successObj ) ) ;
} ) ;
shepherd . scanNativeCoindBins = function ( ) {
let nativeCoindList = { } ;
// check if coind bins are present in agama
for ( let key in nativeCoind ) {
nativeCoindList [ key ] = {
name : nativeCoind [ key ] . name ,
port : nativeCoind [ key ] . port ,
bin : nativeCoind [ key ] . bin ,
bins : {
daemon : false ,
cli : false ,
} ,
} ;
if ( fs . existsSync ( ` ${ coindRootDir } / ${ key } / ${ nativeCoind [ key ] . bin } d ${ os . platform ( ) === 'win32' ? '.exe' : '' } ` ) ) {
nativeCoindList [ key ] . bins . daemon = true ;
}
if ( fs . existsSync ( ` ${ coindRootDir } / ${ key } / ${ nativeCoind [ key ] . bin } -cli ${ os . platform ( ) === 'win32' ? '.exe' : '' } ` ) ) {
nativeCoindList [ key ] . bins . cli = true ;
}
}
return nativeCoindList ;
}
shepherd . getAppRuntimeLog = function ( ) {
return new Promise ( ( resolve , reject ) => {
resolve ( appRuntimeLog ) ;
} ) ;
} ;
shepherd . startSPV = function ( coin ) {
if ( coin === 'KMD+REVS+JUMBLR' ) {
shepherd . addElectrumCoin ( 'KMD' ) ;
shepherd . addElectrumCoin ( 'REVS' ) ;
shepherd . addElectrumCoin ( 'JUMBLR' ) ;
} else {
shepherd . addElectrumCoin ( coin ) ;
}
}
shepherd . startKMDNative = function ( selection , isManual ) {
if ( isManual ) {
shepherd . kmdMainPassiveMode = true ;
}
if ( selection === 'KMD' ) {
const herdData = {
'ac_name' : 'komodod' ,
'ac_options' : [
'-daemon=0' ,
'-addnode=78.47.196.146' ,
] ,
} ;
const options = {
url : ` http://127.0.0.1: ${ shepherd . appConfig . agamaPort } /shepherd/herd ` ,
method : 'POST' ,
headers : {
'Content-Type' : 'application/json' ,
} ,
body : JSON . stringify ( {
herd : 'komodod' ,
options : herdData ,
} )
} ;
request ( options , function ( error , response , body ) {
if ( response &&
response . statusCode &&
response . statusCode === 200 ) {
//resolve(body);
} else {
//resolve(body);
}
} ) ;
} else {
const herdData = [ {
'ac_name' : 'komodod' ,
'ac_options' : [
'-daemon=0' ,
'-addnode=78.47.196.146' ,
]
} , {
'ac_name' : 'REVS' ,
'ac_options' : [
'-daemon=0' ,
'-server' ,
` -ac_name=REVS ` ,
'-addnode=78.47.196.146' ,
'-ac_supply=1300000'
]
} , {
'ac_name' : 'JUMBLR' ,
'ac_options' : [
'-daemon=0' ,
'-server' ,
` -ac_name=JUMBLR ` ,
'-addnode=78.47.196.146' ,
'-ac_supply=999999'
]
} ] ;
for ( let i = 0 ; i < herdData . length ; i ++ ) {
setTimeout ( ( ) => {
const options = {
url : ` http://127.0.0.1: ${ shepherd . appConfig . agamaPort } /shepherd/herd ` ,
method : 'POST' ,
headers : {
'Content-Type' : 'application/json' ,
} ,
body : JSON . stringify ( {
herd : 'komodod' ,
options : herdData [ i ] ,
} )
} ;
request ( options , function ( error , response , body ) {
if ( response &&
response . statusCode &&
response . statusCode === 200 ) {
//resolve(body);
} else {
//resolve(body);
}
} ) ;
} , 100 ) ;
}
}
} ;
/ *
* Combined native dashboard update same as in gui
* type : GET
* params : coin
* /
shepherd . post ( '/native/dashboard/update' , function ( req , res , next ) {
let _ returnObj ;
let _ promiseStack ;
const _ coin = req . body . coin ;
if ( _ coin === 'CHIPS' ) {
_ returnObj = {
getinfo : { } ,
listtransactions : [ ] ,
getbalance : { } ,
listunspent : { } ,
addresses : { } ,
} ;
_ promiseStack = [
'getinfo' ,
'listtransactions' ,
'getbalance' ,
] ;
} else {
_ returnObj = {
getinfo : { } ,
listtransactions : [ ] ,
z_gettotalbalance : { } ,
z_getoperationstatus : { } ,
listunspent : { } ,
addresses : { } ,
} ;
_ promiseStack = [
'getinfo' ,
'listtransactions' ,
'z_gettotalbalance' ,
'z_getoperationstatus'
] ;
}
function getAddressesNative ( coin ) {
const type = [
'public' ,
'private'
] ;
if ( coin === 'CHIPS' ) {
type . pop ( ) ;
}
Promise . all ( type . map ( ( _ type , index ) => {
return new Promise ( ( resolve , reject ) => {
_ bitcoinRPC (
coin ,
_ type === 'public' ? 'getaddressesbyaccount' : 'z_listaddresses' ,
[ '' ]
) . then ( function ( _ json ) {
if ( _ json === 'Work queue depth exceeded' ||
! _ json ) {
resolve ( { error : 'daemon is busy' } ) ;
} else {
resolve ( JSON . parse ( _ json ) . result ) ;
}
} ) ;
} ) ;
} ) )
. then ( result => {
if ( result [ 0 ] &&
result [ 0 ] . length ) {
function calcBalance ( result , json ) {
if ( json &&
json . length ) {
const allAddrArray = json . map ( res => res . address ) . filter ( ( x , i , a ) => a . indexOf ( x ) == i ) ;
for ( let a = 0 ; a < allAddrArray . length ; a ++ ) {
const filteredArray = json . filter ( res => res . address === allAddrArray [ a ] ) . map ( res => res . amount ) ;
let isNewAddr = true ;
for ( let x = 0 ; x < result . length && isNewAddr ; x ++ ) {
for ( let y = 0 ; y < result [ x ] . length && isNewAddr ; y ++ ) {
if ( allAddrArray [ a ] === result [ x ] [ y ] ) {
isNewAddr = false ;
}
}
}
if ( isNewAddr &&
( allAddrArray [ a ] . substring ( 0 , 2 ) === 'zc' ||
allAddrArray [ a ] . substring ( 0 , 2 ) === 'zt' ) ) {
result [ 1 ] [ result [ 1 ] . length ] = allAddrArray [ a ] ;
} else {
result [ 0 ] [ result [ 0 ] . length ] = allAddrArray [ a ] ;
}
}
}
// remove addr duplicates
if ( result [ 0 ] &&
result [ 0 ] . length ) {
result [ 0 ] = result [ 0 ] . filter ( function ( elem , pos ) {
return result [ 0 ] . indexOf ( elem ) === pos ;
} ) ;
}
if ( result [ 1 ] &&
result [ 1 ] . length ) {
result [ 1 ] = result [ 1 ] . filter ( function ( elem , pos ) {
return result [ 1 ] . indexOf ( elem ) === pos ;
} ) ;
}
let newAddressArray = [ ] ;
for ( let a = 0 ; a < result . length ; a ++ ) {
newAddressArray [ a ] = [ ] ;
if ( result [ a ] ) {
for ( let b = 0 ; b < result [ a ] . length ; b ++ ) {
let filteredArray ;
filteredArray = json . filter ( res => res . address === result [ a ] [ b ] ) . map ( res => res . amount ) ;
let sum = 0 ;
for ( let i = 0 ; i < filteredArray . length ; i ++ ) {
sum += filteredArray [ i ] ;
}
newAddressArray [ a ] [ b ] = {
address : result [ a ] [ b ] ,
amount : sum ,
type : a === 0 ? 'public' : 'private' ,
} ;
}
}
}
// get zaddr balance
if ( result [ 1 ] &&
result [ 1 ] . length ) {
Promise . all ( result [ 1 ] . map ( ( _ address , index ) => {
return new Promise ( ( resolve , reject ) => {
_ bitcoinRPC ( coin , 'z_getbalance' , [ _ address ] )
. then ( function ( __ json ) {
__ json = JSON . parse ( __ json ) ;
if ( __ json &&
__ json . error ) {
resolve ( 0 ) ;
} else {
resolve ( __ json . result )
newAddressArray [ 1 ] [ index ] = {
address : _ address ,
amount : __ json . result ,
type : 'private' ,
} ;
}
} ) ;
} ) ;
} ) )
. then ( zresult => {
_ returnObj . addresses = {
public : newAddressArray [ 0 ] ,
private : newAddressArray [ 1 ] ,
} ;
const returnObj = {
msg : 'success' ,
result : _ returnObj ,
} ;
res . end ( JSON . stringify ( returnObj ) ) ;
} ) ;
} else {
_ returnObj . addresses = {
public : newAddressArray [ 0 ] ,
private : newAddressArray [ 1 ] ,
} ;
const returnObj = {
msg : 'success' ,
result : _ returnObj ,
} ;
res . end ( JSON . stringify ( returnObj ) ) ;
}
}
_ bitcoinRPC ( coin , 'listunspent' )
. then ( function ( __ json ) {
if ( __ json === 'Work queue depth exceeded' ||
! __ json ) {
const returnObj = {
msg : 'success' ,
result : _ returnObj ,
} ;
res . end ( JSON . stringify ( returnObj ) ) ;
} else {
_ returnObj . listunspent = JSON . parse ( __ json ) ;
calcBalance (
result ,
JSON . parse ( __ json ) . result
) ;
}
} ) ;
} else {
_ returnObj . addresses = {
public : { } ,
private : { } ,
} ;
const returnObj = {
msg : 'success' ,
result : _ returnObj ,
} ;
res . end ( JSON . stringify ( returnObj ) ) ;
}
} )
}
function _ bitcoinRPC ( coin , cmd , params ) {
return new Promise ( function ( resolve , reject ) {
let _ payload ;
if ( params ) {
_ payload = {
mode : null ,
chain : coin ,
cmd : cmd ,
params : params ,
} ;
} else {
_ payload = {
mode : null ,
chain : coin ,
cmd : cmd ,
} ;
}
const options = {
url : ` http://127.0.0.1: ${ shepherd . appConfig . agamaPort } /shepherd/cli ` ,
method : 'POST' ,
headers : {
'Content-Type' : 'application/json' ,
} ,
body : JSON . stringify ( { payload : _ payload } ) ,
timeout : 120000 ,
} ;
request ( options , function ( error , response , body ) {
if ( response &&
response . statusCode &&
response . statusCode === 200 ) {
resolve ( body ) ;
} else {
resolve ( body ) ;
}
} ) ;
} ) ;
}
Promise . all ( _ promiseStack . map ( ( _ call , index ) => {
let _ params ;
if ( _ call === 'listtransactions' ) {
_ params = [
'' ,
300 ,
0
] ;
}
return new Promise ( ( resolve , reject ) => {
_ bitcoinRPC (
_ coin ,
_ call ,
_ params
)
. then ( function ( json ) {
if ( json === 'Work queue depth exceeded' ||
! json ) {
_ returnObj [ _ call ] = { error : 'daemon is busy' } ;
} else {
_ returnObj [ _ call ] = JSON . parse ( json ) ;
}
resolve ( json ) ;
} ) ;
} ) ;
} ) )
. then ( result => {
getAddressesNative ( _ coin ) ;
} ) ;
} ) ;
shepherd . testClearAll = function ( ) {
return new Promise ( function ( resolve , reject ) {
fs . removeSync ( ` ${ iguanaTestDir } ` ) ;
resolve ( 'done' ) ;
} ) ;
}
shepherd . testBins = function ( daemonName ) {
return new Promise ( function ( resolve , reject ) {
const _ bins = {
komodod : komododBin ,
komodoCli : komodocliBin ,
} ;
const _ arg = null ;
let _ pid ;
shepherd . log ( 'testBins exec ' + _ bins [ daemonName ] ) ;
if ( ! fs . existsSync ( agamaTestDir ) ) {
fs . mkdirSync ( agamaTestDir ) ;
}
try {
_ fs . access ( ` ${ agamaTestDir } / ${ daemonName } Test.log ` , fs . constants . R_OK , function ( err ) {
if ( ! err ) {
try {
_ fs . unlinkSync ( ` ${ agamaTestDir } / ${ daemonName } Test.log ` ) ;
} catch ( e ) { }
} else {
shepherd . log ( ` path ${ agamaTestDir } / ${ daemonName } Test.log doesnt exist ` ) ;
}
} ) ;
} catch ( e ) { }
if ( daemonName === 'komodod' ) {
try {
_ fs . access ( ` ${ iguanaTestDir } /debug.log ` , fs . constants . R_OK , function ( err ) {
if ( ! err ) {
_ fs . unlinkSync ( ` ${ iguanaTestDir } /db.log ` ) ;
_ fs . unlinkSync ( ` ${ iguanaTestDir } /debug.log ` ) ;
_ fs . unlinkSync ( ` ${ iguanaTestDir } /komodo.conf ` ) ;
_ fs . unlinkSync ( ` ${ iguanaTestDir } /komodod.pid ` ) ;
_ fs . unlinkSync ( ` ${ iguanaTestDir } /komodostate ` ) ;
_ fs . unlinkSync ( ` ${ iguanaTestDir } /realtime ` ) ;
_ fs . unlinkSync ( ` ${ iguanaTestDir } /wallet.dat ` ) ;
_ fs . unlinkSync ( ` ${ iguanaTestDir } /.lock ` ) ;
fs . removeSync ( ` ${ iguanaTestDir } /blocks ` ) ;
fs . removeSync ( ` ${ iguanaTestDir } /chainstate ` ) ;
fs . removeSync ( ` ${ iguanaTestDir } /database ` ) ;
execKomodod ( ) ;
} else {
shepherd . log ( ` test: nothing to remove in ${ iguanaTestDir } ` ) ;
execKomodod ( ) ;
}
} ) ;
} catch ( e ) { }
function execKomodod ( ) {
let _ komododTest = {
port : 'unknown' ,
start : 'unknown' ,
getinfo : 'unknown' ,
errors : {
assertFailed : false ,
zcashParams : false ,
} ,
} ;
const _ komodoConf = 'rpcuser=user83f3afba8d714993\n' +
'rpcpassword=0d4430ca1543833e35bce5a0cc9e16b3\n' +
'server=1\n' +
'addnode=78.47.196.146\n' +
'addnode=5.9.102.210\n' +
'addnode=178.63.69.164\n' +
'addnode=88.198.65.74\n' +
'addnode=5.9.122.241\n' +
'addnode=144.76.94.3\n' +
'addnode=144.76.94.38\n' +
'addnode=89.248.166.91\n' +
'addnode=148.251.57.148\n' +
'addnode=149.56.28.84\n' +
'addnode=176.9.26.39\n' +
'addnode=94.102.63.199\n' +
'addnode=94.102.63.200\n' +
'addnode=104.255.64.3\n' +
'addnode=221.121.144.140\n' +
'addnode=103.18.58.150\n' +
'addnode=103.18.58.146\n' +
'addnode=213.202.253.10\n' +
'addnode=185.106.121.32\n' +
'addnode=27.100.36.201\n' ;
fs . writeFile ( ` ${ iguanaTestDir } /komodo.conf ` , _ komodoConf , function ( err ) {
if ( err ) {
shepherd . log ( ` test: error writing komodo conf in ${ iguanaTestDir } ` ) ;
}
} ) ;
portscanner . checkPortStatus ( '7771' , '127.0.0.1' , function ( error , status ) {
// Status is 'open' if currently in use or 'closed' if available
if ( status === 'closed' ) {
_ komododTest . port = 'passed' ;
} else {
_ komododTest . port = 'failed' ;
}
} ) ;
/*pm2.connect(true,function(err) { / / start up pm2 god
if ( err ) {
shepherd . error ( err ) ;
process . exit ( 2 ) ;
}
pm2 . start ( {
script : komododBin , // path to binary
name : 'komodod' ,
exec_mode : 'fork' ,
args : [
'-daemon=0' ,
'-addnode=78.47.196.146' ,
` -datadir= ${ iguanaTestDir } / `
] ,
output : ` ${ iguanaTestDir } /komododTest.log ` ,
mergeLogs : true ,
} , function ( err , apps ) {
if ( apps [ 0 ] &&
apps [ 0 ] . process &&
apps [ 0 ] . process . pid ) {
_ komododTest . start = 'success' ;
shepherd . log ( ` test: got komodod instance pid = ${ apps [ 0 ] . process . pid } ` ) ;
shepherd . writeLog ( ` test: komodod started with pid ${ apps [ 0 ] . process . pid } ` ) ;
} else {
_ komododTest . start = 'failed' ;
shepherd . log ( ` unable to start komodod ` ) ;
}
pm2 . disconnect ( ) ; // Disconnect from PM2
if ( err ) {
shepherd . writeLog ( ` test: error starting komodod ` ) ;
shepherd . log ( ` komodod fork err: ${ err } ` ) ;
// throw err;
}
} ) ;
} ) ; * /
setTimeout ( function ( ) {
const options = {
url : ` http://localhost:7771 ` ,
method : 'POST' ,
auth : {
user : 'user83f3afba8d714993' ,
pass : '0d4430ca1543833e35bce5a0cc9e16b3' ,
} ,
body : JSON . stringify ( {
agent : 'bitcoinrpc' ,
method : 'getinfo' ,
} ) ,
} ;
request ( options , function ( error , response , body ) {
if ( response &&
response . statusCode &&
response . statusCode === 200 ) {
// res.end(body);
shepherd . log ( JSON . stringify ( body , null , '\t' ) ) ;
} else {
// res.end(body);
shepherd . log ( JSON . stringify ( body , null , '\t' ) ) ;
}
} ) ;
} , 10000 ) ;
setTimeout ( function ( ) {
pm2 . delete ( 'komodod' ) ;
resolve ( _ komododTest ) ;
} , 20000 ) ;
}
// komodod debug.log hooks
//"{\"result\":{\"version\":1000850,\"protocolversion\":170002,\"KMDversion\":\"0.1.1\",\"notarized\":0,\"notarizedhash\":\"0000000000000000000000000000000000000000000000000000000000000000\",\"notarizedtxid\":\"0000000000000000000000000000000000000000000000000000000000000000\",\"notarizedtxid_height\":\"mempool\",\"notarized_confirms\":0,\"walletversion\":60000,\"balance\":0.00000000,\"interest\":0.00000000,\"blocks\":128,\"longestchain\":472331,\"timeoffset\":0,\"tiptime\":1473827710,\"connections\":1,\"proxy\":\"\",\"difficulty\":1,\"testnet\":false,\"keypoololdest\":1504118047,\"keypoolsize\":101,\"paytxfee\":0.00000000,\"relayfee\":0.00000100,\"errors\":\"\"},\"error\":null,\"id\":null}\n"
//2017-08-30 17:51:33 Error: Cannot find the Zcash network parameters in the following directory:
//"/home/pbca/.zcash-params"
//Please run 'zcash-fetch-params' or './zcutil/fetch-params.sh' and then restart.
//EXCEPTION: St13runtime_error
//Assertion failed.
//2017-08-30 17:51:14 Using config file /home/pbca/.iguana/test/komodo.conf
//2017-08-30 18:23:43 UpdateTip: new best=0a47c1323f393650f7221c217d19d149d002d35444f47fde61be2dd90fbde8e6 height=1 log2_work=5.0874628 tx=2 date=2016-09-13 19:04:01 progress=0.000001 cache=0.0MiB(1tx)
//2017-08-30 18:23:43 UpdateTip: new best=05076a4e1fc9af0f5fda690257b17ae20c12d4796dfba1624804d012c9ec00be height=2 log2_work=5.6724253 tx=3 date=2016-09-13 19:05:28 progress=0.000001 cache=0.0MiB(2tx)
/ * e x e c F i l e ( ` $ { k o m o d o d B i n } ` , _ a r g , {
maxBuffer : 1024 * 10000 // 10 mb
} , function ( error , stdout , stderr ) {
shepherd . writeLog ( ` stdout: ${ stdout } ` ) ;
shepherd . writeLog ( ` stderr: ${ stderr } ` ) ;
if ( error !== null ) {
console . log ( ` exec error: ${ error } ` ) ;
shepherd . writeLog ( ` exec error: ${ error } ` ) ;
if ( error . toString ( ) . indexOf ( 'using -reindex' ) > - 1 ) {
shepherd . io . emit ( 'service' , {
komodod : {
error : 'run -reindex' ,
}
} ) ;
}
}
} ) ; * /
}
} ) ;
}
// komodod datadir location test
shepherd . testLocation = function ( path ) {
return new Promise ( function ( resolve , reject ) {
fs . lstat ( path , ( err , stats ) => {
if ( err ) {
shepherd . log ( ` error testing path ${ path } ` ) ;
resolve ( - 1 ) ;
} else {
if ( stats . isDirectory ( ) ) {
resolve ( true ) ;
} else {
shepherd . log ( ` error testing path ${ path } not a folder ` ) ;
resolve ( false ) ;
}
}
} ) ;
} ) ;
}
// osx and linux
shepherd . binFixRights = function ( ) {
const osPlatform = os . platform ( ) ;
const _ bins = [
komododBin ,
komodocliBin
] ;
if ( osPlatform === 'darwin' ||
osPlatform === 'linux' ) {
for ( let i = 0 ; i < _ bins . length ; i ++ ) {
_ fs . stat ( _ bins [ i ] , function ( err , stat ) {
if ( ! err ) {
if ( parseInt ( stat . mode . toString ( 8 ) , 10 ) !== 100775 ) {
shepherd . log ( ` ${ _ bins [ i ] } fix permissions ` ) ;
fsnode . chmodSync ( _ bins [ i ] , '0775' ) ;
}
} else {
shepherd . log ( ` error: ${ _ bins [ i ] } not found ` ) ;
}
} ) ;
}
}
}
shepherd . killRogueProcess = function ( processName ) {
// kill rogue process copies on start
let processGrep ;
const osPlatform = os . platform ( ) ;
switch ( osPlatform ) {
case 'darwin' :
processGrep = "ps -p $(ps -A | grep -m1 " + processName + " | awk '{print $1}') | grep -i " + processName ;
break ;
case 'linux' :
processGrep = 'ps -p $(pidof ' + processName + ') | grep -i ' + processName ;
break ;
case 'win32' :
processGrep = 'tasklist' ;
break ;
}
exec ( processGrep , function ( error , stdout , stderr ) {
if ( stdout . indexOf ( processName ) > - 1 ) {
const pkillCmd = osPlatform === 'win32' ? ` taskkill /f /im ${ processName } .exe ` : ` pkill -15 ${ processName } ` ;
shepherd . log ( ` found another ${ processName } process(es) ` ) ;
shepherd . writeLog ( ` found another ${ processName } process(es) ` ) ;
exec ( pkillCmd , function ( error , stdout , stderr ) {
shepherd . log ( ` ${ pkillCmd } is issued ` ) ;
shepherd . writeLog ( ` ${ pkillCmd } is issued ` ) ;
if ( error !== null ) {
shepherd . log ( ` ${ pkillCmd } exec error: ${ error } ` ) ;
shepherd . writeLog ( ` ${ pkillCmd } exec error: ${ error } ` ) ;
} ;
} ) ;
}
if ( error !== null ) {
shepherd . log ( ` ${ processGrep } exec error: ${ error } ` ) ;
shepherd . writeLog ( ` ${ processGrep } exec error: ${ error } ` ) ;
} ;
} ) ;
}
shepherd . zcashParamsExist = function ( ) {
let _ checkList = {
rootDir : _ fs . existsSync ( zcashParamsDir ) ,
provingKey : _ fs . existsSync ( ` ${ zcashParamsDir } /sprout-proving.key ` ) ,
provingKeySize : false ,
verifyingKey : _ fs . existsSync ( ` ${ zcashParamsDir } /sprout-verifying.key ` ) ,
verifyingKeySize : false ,
errors : false ,
} ;
if ( _ checkList . rootDir &&
_ checkList . provingKey ||
_ checkList . verifyingKey ) {
// verify each key size
const _ provingKeySize = _ checkList . provingKey ? fs . lstatSync ( ` ${ zcashParamsDir } /sprout-proving.key ` ) : 0 ;
const _ verifyingKeySize = _ checkList . verifyingKey ? fs . lstatSync ( ` ${ zcashParamsDir } /sprout-verifying.key ` ) : 0 ;
if ( Number ( _ provingKeySize . size ) === 910173851 ) { // bytes
_ checkList . provingKeySize = true ;
}
if ( Number ( _ verifyingKeySize . size ) === 1449 ) {
_ checkList . verifyingKeySize = true ;
}
shepherd . log ( 'zcashparams exist' ) ;
} else {
shepherd . log ( 'zcashparams doesnt exist' ) ;
}
if ( ! _ checkList . rootDir ||
! _ checkList . provingKey ||
! _ checkList . verifyingKey ||
! _ checkList . provingKeySize ||
! _ checkList . verifyingKeySize ) {
_ checkList . errors = true ;
}
return _ checkList ;
}
shepherd . readVersionFile = function ( ) {
// read app version
const rootLocation = path . join ( __ dirname , '../' ) ;
const localVersionFile = fs . readFileSync ( ` ${ rootLocation } version ` , 'utf8' ) ;
return localVersionFile ;
}
shepherd . createAgamaDirs = function ( ) {
if ( ! fs . existsSync ( agamaDir ) ) {
fs . mkdirSync ( agamaDir ) ;
if ( fs . existsSync ( agamaDir ) ) {
shepherd . log ( ` created agama folder at ${ agamaDir } ` ) ;
shepherd . writeLog ( ` created agama folder at ${ agamaDir } ` ) ;
}
} else {
shepherd . log ( 'agama folder already exists' ) ;
}
if ( ! fs . existsSync ( ` ${ agamaDir } /shepherd ` ) ) {
fs . mkdirSync ( ` ${ agamaDir } /shepherd ` ) ;
if ( fs . existsSync ( ` ${ agamaDir } /shepherd ` ) ) {
shepherd . log ( ` created shepherd folder at ${ agamaDir } /shepherd ` ) ;
shepherd . writeLog ( ` create shepherd folder at ${ agamaDir } /shepherd ` ) ;
}
} else {
shepherd . log ( 'agama/shepherd folder already exists' ) ;
}
if ( ! fs . existsSync ( ` ${ agamaDir } /shepherd/pin ` ) ) {
fs . mkdirSync ( ` ${ agamaDir } /shepherd/pin ` ) ;
if ( fs . existsSync ( ` ${ agamaDir } /shepherd/pin ` ) ) {
shepherd . log ( ` created pin folder at ${ agamaDir } /shepherd/pin ` ) ;
shepherd . writeLog ( ` create pin folder at ${ agamaDir } /shepherd/pin ` ) ;
}
} else {
shepherd . log ( 'shepherd/pin folder already exists' ) ;
}
}
/ *
* type : POST
* params : none
* /
shepherd . post ( '/encryptkey' , function ( req , res , next ) {
if ( req . body . key &&
req . body . string &&
req . body . pubkey ) {
const encryptedString = aes256 . encrypt ( req . body . key , req . body . string ) ;
// test pin security
// - at least 1 char in upper case
// - at least 1 digit
// - at least one special character
// - min length 8
const _ pin = req . body . key ;
const _ pinTest = _ pin . match ( '^(?=.*[A-Z])(?=.*[^<>{}\"/|;:.,~!?@#$%^=&*\\]\\\\()\\[_+]*$)(?=.*[0-9])(?=.*[a-z]).{8}$' ) ;
fs . writeFile ( ` ${ agamaDir } /shepherd/pin/ ${ req . body . pubkey } .pin ` , encryptedString , function ( err ) {
if ( err ) {
shepherd . log ( 'error writing pin file' ) ;
}
const returnObj = {
msg : 'success' ,
result : encryptedString ,
} ;
res . end ( JSON . stringify ( returnObj ) ) ;
} ) ;
} else {
let errorObj = {
msg : 'error' ,
result : '' ,
} ;
const _ paramsList = [
'key' ,
'string' ,
'pubkey'
] ;
let _ errorParamsList = [ ] ;
for ( let i = 0 ; i < _ paramsList . length ; i ++ ) {
if ( ! req . query [ _ paramsList [ i ] ] ) {
_ errorParamsList . push ( _ paramsList [ i ] ) ;
}
}
errorObj . result = ` missing param ${ _ errorParamsList . join ( ', ' ) } ` ;
res . end ( JSON . stringify ( errorObj ) ) ;
}
} ) ;
shepherd . post ( '/decryptkey' , function ( req , res , next ) {
if ( req . body . key &&
req . body . pubkey ) {
if ( fs . existsSync ( ` ${ agamaDir } /shepherd/pin/ ${ req . body . pubkey } .pin ` ) ) {
fs . readFile ( ` ${ agamaDir } /shepherd/pin/ ${ req . body . pubkey } .pin ` , 'utf8' , function ( err , data ) {
if ( err ) {
const errorObj = {
msg : 'error' ,
result : err ,
} ;
res . end ( JSON . stringify ( errorObj ) ) ;
} else {
const encryptedKey = aes256 . decrypt ( req . body . key , data ) ;
// test if stored encrypted passphrase is decrypted correctly
// if not then the key is wrong
const _ regexTest = encryptedKey . match ( /^[0-9a-zA-Z ]+$/g ) ;
let returnObj ;
if ( ! _ regexTest ) {
returnObj = {
msg : 'error' ,
result : 'wrong key' ,
} ;
} else {
returnObj = {
msg : 'success' ,
result : encryptedKey ,
} ;
}
res . end ( JSON . stringify ( returnObj ) ) ;
}
} ) ;
} else {
const errorObj = {
msg : 'error' ,
result : ` file ${ req . query . pubkey } .pin doesnt exist ` ,
} ;
res . end ( JSON . stringify ( errorObj ) ) ;
}
} else {
const errorObj = {
msg : 'error' ,
result : 'missing key or pubkey param' ,
} ;
res . end ( JSON . stringify ( errorObj ) ) ;
}
} ) ;
shepherd . get ( '/getpinlist' , function ( req , res , next ) {
if ( fs . existsSync ( ` ${ agamaDir } /shepherd/pin ` ) ) {
fs . readdir ( ` ${ agamaDir } /shepherd/pin ` , function ( err , items ) {
let _ pins = [ ] ;
for ( let i = 0 ; i < items . length ; i ++ ) {
if ( items [ i ] . substr ( items [ i ] . length - 4 , 4 ) === '.pin' ) {
_ pins . push ( items [ i ] . substr ( 0 , items [ i ] . length - 4 ) ) ;
}
}
if ( ! items . length ) {
const errorObj = {
msg : 'error' ,
result : 'no pins' ,
} ;
res . end ( JSON . stringify ( errorObj ) ) ;
} else {
const successObj = {
msg : 'success' ,
result : _ pins ,
} ;
res . end ( JSON . stringify ( successObj ) ) ;
}
} ) ;
} else {
const errorObj = {
msg : 'error' ,
result : 'pin folder doesnt exist' ,
} ;
res . end ( JSON . stringify ( errorObj ) ) ;
}
} ) ;
/ * *
* Promise based download file method
* /
function downloadFile ( configuration ) {
return new Promise ( function ( resolve , reject ) {
// Save variable to know progress
let receivedBytes = 0 ;
let totalBytes = 0 ;
let req = request ( {
method : 'GET' ,
uri : configuration . remoteFile ,
agentOptions : {
keepAlive : true ,
keepAliveMsecs : 15000 ,
} ,
} ) ;
let out = fs . createWriteStream ( configuration . localFile ) ;
req . pipe ( out ) ;
req . on ( 'response' , function ( data ) {
// Change the total bytes value to get progress later.
totalBytes = parseInt ( data . headers [ 'content-length' ] ) ;
} ) ;
// Get progress if callback exists
if ( configuration . hasOwnProperty ( 'onProgress' ) ) {
req . on ( 'data' , function ( chunk ) {
// Update the received bytes
receivedBytes += chunk . length ;
configuration . onProgress ( receivedBytes , totalBytes ) ;
} ) ;
} else {
req . on ( 'data' , function ( chunk ) {
// Update the received bytes
receivedBytes += chunk . length ;
} ) ;
}
req . on ( 'end' , function ( ) {
resolve ( ) ;
} ) ;
} ) ;
}
const remoteBinLocation = {
win32 : 'https://artifacts.supernet.org/latest/windows/' ,
darwin : 'https://artifacts.supernet.org/latest/osx/' ,
linux : 'https://artifacts.supernet.org/latest/linux/' ,
} ;
const localBinLocation = {
win32 : 'assets/bin/win64/' ,
darwin : 'assets/bin/osx/' ,
linux : 'assets/bin/linux64/' ,
} ;
const latestBins = {
win32 : [
'komodo-cli.exe' ,
'komodod.exe' ,
'libcrypto-1_1.dll' ,
'libcurl-4.dll' ,
'libcurl.dll' ,
'libgcc_s_sjlj-1.dll' ,
'libnanomsg.dll' ,
'libssl-1_1.dll' ,
'libwinpthread-1.dll' ,
'nanomsg.dll' ,
'pthreadvc2.dll' ,
] ,
darwin : [
'komodo-cli' ,
'komodod' ,
'libgcc_s.1.dylib' ,
'libgomp.1.dylib' ,
'libnanomsg.5.0.0.dylib' ,
'libstdc++.6.dylib' , // encode %2B
] ,
linux : [
'komodo-cli' ,
'komodod' ,
] ,
} ;
let binsToUpdate = [ ] ;
/ *
* Check bins file size
* type :
* params :
* /
// TODO: promises
shepherd . get ( '/update/bins/check' , function ( req , res , next ) {
const rootLocation = path . join ( __ dirname , '../' ) ;
const successObj = {
msg : 'success' ,
result : 'bins' ,
} ;
res . end ( JSON . stringify ( successObj ) ) ;
const _ os = os . platform ( ) ;
shepherd . log ( ` checking bins: ${ _ os } ` ) ;
shepherd . io . emit ( 'patch' , {
patch : {
type : 'bins-check' ,
status : 'progress' ,
message : ` checking bins: ${ _ os } ` ,
} ,
} ) ;
// get list of bins/dlls that can be updated to the latest
for ( let i = 0 ; i < latestBins [ _ os ] . length ; i ++ ) {
remoteFileSize ( remoteBinLocation [ _ os ] + latestBins [ _ os ] [ i ] , function ( err , remoteBinSize ) {
const localBinSize = fs . statSync ( rootLocation + localBinLocation [ _ os ] + latestBins [ _ os ] [ i ] ) . size ;
shepherd . log ( 'remote url: ' + ( remoteBinLocation [ _ os ] + latestBins [ _ os ] [ i ] ) + ' (' + remoteBinSize + ')' ) ;
shepherd . log ( 'local file: ' + ( rootLocation + localBinLocation [ _ os ] + latestBins [ _ os ] [ i ] ) + ' (' + localBinSize + ')' ) ;
if ( remoteBinSize !== localBinSize ) {
shepherd . log ( ` ${ latestBins [ _ os ] [ i ] } can be updated ` ) ;
binsToUpdate . push ( {
name : latestBins [ _ os ] [ i ] ,
rSize : remoteBinSize ,
lSize : localBinSize ,
} ) ;
}
if ( i === latestBins [ _ os ] . length - 1 ) {
shepherd . io . emit ( 'patch' , {
patch : {
type : 'bins-check' ,
status : 'done' ,
fileList : binsToUpdate ,
} ,
} ) ;
}
} ) ;
}
} ) ;
/ *
* Update bins
* type :
* params :
* /
shepherd . get ( '/update/bins' , function ( req , res , next ) {
const rootLocation = path . join ( __ dirname , '../' ) ;
const _ os = os . platform ( ) ;
const successObj = {
msg : 'success' ,
result : {
filesCount : binsToUpdate . length ,
list : binsToUpdate ,
} ,
} ;
res . end ( JSON . stringify ( successObj ) ) ;
for ( let i = 0 ; i < binsToUpdate . length ; i ++ ) {
downloadFile ( {
remoteFile : remoteBinLocation [ _ os ] + binsToUpdate [ i ] . name ,
localFile : ` ${ rootLocation } ${ localBinLocation [ _ os ] } patch/ ${ binsToUpdate [ i ] . name } ` ,
onProgress : function ( received , total ) {
const percentage = ( received * 100 ) / total ;
if ( percentage . toString ( ) . indexOf ( '.10' ) > - 1 ) {
shepherd . io . emit ( 'patch' , {
msg : {
type : 'bins-update' ,
status : 'progress' ,
file : binsToUpdate [ i ] . name ,
bytesTotal : total ,
bytesReceived : received ,
} ,
} ) ;
// shepherd.log(`${binsToUpdate[i].name} ${percentage}% | ${received} bytes out of ${total} bytes.`);
}
}
} )
. then ( function ( ) {
// verify that remote file is matching to DL'ed file
const localBinSize = fs . statSync ( ` ${ rootLocation } ${ localBinLocation [ _ os ] } patch/ ${ binsToUpdate [ i ] . name } ` ) . size ;
shepherd . log ( 'compare dl file size' ) ;
if ( localBinSize === binsToUpdate [ i ] . rSize ) {
shepherd . io . emit ( 'patch' , {
msg : {
type : 'bins-update' ,
file : binsToUpdate [ i ] . name ,
status : 'done' ,
} ,
} ) ;
shepherd . log ( ` file ${ binsToUpdate [ i ] . name } succesfully downloaded ` ) ;
} else {
shepherd . io . emit ( 'patch' , {
msg : {
type : 'bins-update' ,
file : binsToUpdate [ i ] . name ,
message : 'size mismatch' ,
} ,
} ) ;
shepherd . log ( ` error: ${ binsToUpdate [ i ] . name } file size doesnt match remote! ` ) ;
}
} ) ;
}
} ) ;
/ *
* DL app patch
* type : GET
* params : patchList
* /
shepherd . get ( '/update/patch' , function ( req , res , next ) {
const successObj = {
msg : 'success' ,
result : 'dl started'
} ;
res . end ( JSON . stringify ( successObj ) ) ;
shepherd . updateAgama ( ) ;
} ) ;
shepherd . updateAgama = function ( ) {
const rootLocation = path . join ( __ dirname , '../' ) ;
downloadFile ( {
remoteFile : 'https://github.com/pbca26/dl-test/raw/master/patch.zip' ,
localFile : ` ${ rootLocation } patch.zip ` ,
onProgress : function ( received , total ) {
const percentage = ( received * 100 ) / total ;
if ( percentage . toString ( ) . indexOf ( '.10' ) > - 1 ) {
shepherd . io . emit ( 'patch' , {
msg : {
status : 'progress' ,
type : 'ui' ,
progress : percentage ,
bytesTotal : total ,
bytesReceived : received ,
} ,
} ) ;
//shepherd.log(`patch ${percentage}% | ${received} bytes out of ${total} bytes.`);
}
}
} )
. then ( function ( ) {
remoteFileSize ( 'https://github.com/pbca26/dl-test/raw/master/patch.zip' , function ( err , remotePatchSize ) {
// verify that remote file is matching to DL'ed file
const localPatchSize = fs . statSync ( ` ${ rootLocation } patch.zip ` ) . size ;
shepherd . log ( 'compare dl file size' ) ;
if ( localPatchSize === remotePatchSize ) {
const zip = new AdmZip ( ` ${ rootLocation } patch.zip ` ) ;
shepherd . log ( 'patch succesfully downloaded' ) ;
shepherd . log ( 'extracting contents' ) ;
if ( shepherd . appConfig . dev ) {
if ( ! fs . existsSync ( ` ${ rootLocation } /patch ` ) ) {
fs . mkdirSync ( ` ${ rootLocation } /patch ` ) ;
}
}
zip . extractAllTo ( /*target path*/ rootLocation + ( shepherd . appConfig . dev ? '/patch' : '' ) , /*overwrite*/ true ) ;
// TODO: extract files in chunks
shepherd . io . emit ( 'patch' , {
msg : {
type : 'ui' ,
status : 'done' ,
} ,
} ) ;
fs . unlinkSync ( ` ${ rootLocation } patch.zip ` ) ;
} else {
shepherd . io . emit ( 'patch' , {
msg : {
type : 'ui' ,
status : 'error' ,
message : 'size mismatch' ,
} ,
} ) ;
shepherd . log ( 'patch file size doesnt match remote!' ) ;
}
} ) ;
} ) ;
}
/ *
* check latest version
* type :
* params :
* /
shepherd . get ( '/update/patch/check' , function ( req , res , next ) {
const rootLocation = path . join ( __ dirname , '../' ) ;
const options = {
url : 'https://github.com/pbca26/dl-test/raw/master/version' ,
method : 'GET' ,
} ;
request ( options , function ( error , response , body ) {
if ( response &&
response . statusCode &&
response . statusCode === 200 ) {
const remoteVersion = body . split ( '\n' ) ;
const localVersionFile = fs . readFileSync ( ` ${ rootLocation } version ` , 'utf8' ) ;
let localVersion ;
if ( localVersionFile . indexOf ( '\r\n' ) > - 1 ) {
localVersion = localVersionFile . split ( '\r\n' ) ;
} else {
localVersion = localVersionFile . split ( '\n' ) ;
}
if ( remoteVersion [ 0 ] === localVersion [ 0 ] ) {
const successObj = {
msg : 'success' ,
result : 'latest' ,
} ;
res . end ( JSON . stringify ( successObj ) ) ;
} else {
const successObj = {
msg : 'success' ,
result : 'update' ,
version : {
local : localVersion [ 0 ] ,
remote : remoteVersion [ 0 ] ,
} ,
} ;
res . end ( JSON . stringify ( successObj ) ) ;
}
} else {
res . end ( {
err : 'error getting update' ,
} ) ;
}
} ) ;
} ) ;
/ *
* unpack zip
* type :
* params :
* /
shepherd . get ( '/unpack' , function ( req , res , next ) {
const dlLocation = path . join ( __ dirname , '../' ) ;
const zip = new AdmZip ( ` ${ dlLocation } patch.zip ` ) ;
zip . extractAllTo ( /*target path*/ ` ${ dlLocation } /patch/unpack ` , /*overwrite*/ true ) ;
const successObj = {
msg : 'success' ,
result : 'unpack started' ,
} ;
res . end ( JSON . stringify ( successObj ) ) ;
} ) ;
/ *
* Update bins
* type :
* params :
* /
shepherd . get ( '/zcparamsdl' , function ( req , res , next ) {
// const dlLocation = zcashParamsDir + '/test';
const dlLocation = zcashParamsDir ;
const dlOption = req . query . dloption ;
const successObj = {
msg : 'success' ,
result : 'zcash params dl started' ,
} ;
res . end ( JSON . stringify ( successObj ) ) ;
for ( let key in zcashParamsDownloadLinks [ dlOption ] ) {
downloadFile ( {
remoteFile : zcashParamsDownloadLinks [ dlOption ] [ key ] ,
localFile : dlLocation + '/' + 'sprout-' + key + '.key' ,
onProgress : function ( received , total ) {
const percentage = ( received * 100 ) / total ;
if ( percentage . toString ( ) . indexOf ( '.10' ) > - 1 ) {
shepherd . io . emit ( 'zcparams' , {
msg : {
type : 'zcpdownload' ,
status : 'progress' ,
file : key ,
bytesTotal : total ,
bytesReceived : received ,
progress : percentage ,
} ,
} ) ;
// shepherd.log(`${key} ${percentage}% | ${received} bytes out of ${total} bytes.`);
}
}
} )
. then ( function ( ) {
const checkZcashParams = shepherd . zcashParamsExist ( ) ;
shepherd . log ( ` ${ key } dl done ` ) ;
if ( checkZcashParams . error ) {
shepherd . io . emit ( 'zcparams' , {
msg : {
type : 'zcpdownload' ,
file : key ,
status : 'error' ,
message : 'size mismatch' ,
progress : 100 ,
} ,
} ) ;
} else {
shepherd . io . emit ( 'zcparams' , {
msg : {
type : 'zcpdownload' ,
file : key ,
progress : 100 ,
status : 'done' ,
} ,
} ) ;
shepherd . log ( ` file ${ key } succesfully downloaded ` ) ;
}
} ) ;
}
} ) ;
/ *
* type : GET
*
* /
shepherd . get ( '/coinslist' , function ( req , res , next ) {
if ( fs . existsSync ( ` ${ agamaDir } /shepherd/coinslist.json ` ) ) {
fs . readFile ( ` ${ agamaDir } /shepherd/coinslist.json ` , 'utf8' , function ( err , data ) {
if ( err ) {
const errorObj = {
msg : 'error' ,
result : err ,
} ;
res . end ( JSON . stringify ( errorObj ) ) ;
} else {
const successObj = {
msg : 'success' ,
result : data ? JSON . parse ( data ) : '' ,
} ;
res . end ( JSON . stringify ( successObj ) ) ;
}
} ) ;
} else {
const errorObj = {
msg : 'error' ,
result : 'coin list doesn\'t exist' ,
} ;
res . end ( JSON . stringify ( errorObj ) ) ;
}
} ) ;
/ *
* type : POST
* params : payload
* /
shepherd . post ( '/guilog' , function ( req , res , next ) {
const logLocation = ` ${ agamaDir } /shepherd ` ;
if ( ! guiLog [ shepherd . appSessionHash ] ) {
guiLog [ shepherd . appSessionHash ] = { } ;
}
if ( guiLog [ shepherd . appSessionHash ] [ req . body . timestamp ] ) {
guiLog [ shepherd . appSessionHash ] [ req . body . timestamp ] . status = req . body . status ;
guiLog [ shepherd . appSessionHash ] [ req . body . timestamp ] . response = req . body . response ;
} else {
guiLog [ shepherd . appSessionHash ] [ req . body . timestamp ] = {
function : req . body . function ,
type : req . body . type ,
url : req . body . url ,
payload : req . body . payload ,
status : req . body . status ,
} ;
}
fs . writeFile ( ` ${ logLocation } /agamalog.json ` , JSON . stringify ( guiLog ) , function ( err ) {
if ( err ) {
shepherd . writeLog ( 'error writing gui log file' ) ;
}
const returnObj = {
msg : 'success' ,
result : 'gui log entry is added' ,
} ;
res . end ( JSON . stringify ( returnObj ) ) ;
} ) ;
} ) ;
/ *
* type : GET
* params : type
* /
shepherd . get ( '/getlog' , function ( req , res , next ) {
const logExt = req . query . type === 'txt' ? 'txt' : 'json' ;
if ( fs . existsSync ( ` ${ agamaDir } /shepherd/agamalog. ${ logExt } ` ) ) {
fs . readFile ( ` ${ agamaDir } /shepherd/agamalog. ${ logExt } ` , 'utf8' , function ( err , data ) {
if ( err ) {
const errorObj = {
msg : 'error' ,
result : err ,
} ;
res . end ( JSON . stringify ( errorObj ) ) ;
} else {
const successObj = {
msg : 'success' ,
result : data ? JSON . parse ( data ) : '' ,
} ;
res . end ( JSON . stringify ( successObj ) ) ;
}
} ) ;
} else {
const errorObj = {
msg : 'error' ,
result : ` agama. ${ logExt } doesnt exist ` ,
} ;
res . end ( JSON . stringify ( errorObj ) ) ;
}
} ) ;
/ *
* type : POST
* params : payload
* /
shepherd . post ( '/coinslist' , function ( req , res , next ) {
const _ payload = req . body . payload ;
if ( ! _ payload ) {
const errorObj = {
msg : 'error' ,
result : 'no payload provided' ,
} ;
res . end ( JSON . stringify ( errorObj ) ) ;
} else {
fs . writeFile ( ` ${ agamaDir } /shepherd/coinslist.json ` , JSON . stringify ( _ payload ) , function ( err ) {
if ( err ) {
const errorObj = {
msg : 'error' ,
result : err ,
} ;
res . end ( JSON . stringify ( errorObj ) ) ;
} else {
const successObj = {
msg : 'success' ,
result : 'done' ,
} ;
res . end ( JSON . stringify ( successObj ) ) ;
}
} ) ;
}
} ) ;
// TODO: check if komodod is running
shepherd . quitKomodod = function ( timeout = 100 ) {
// if komodod is under heavy load it may not respond to cli stop the first time
// exit komodod gracefully
let coindExitInterval = { } ;
lockDownAddCoin = true ;
for ( let key in coindInstanceRegistry ) {
const chain = key !== 'komodod' ? key : null ;
let _ coindQuitCmd = komodocliBin ;
// any coind
if ( shepherd . nativeCoindList [ key . toLowerCase ( ) ] ) {
_ coindQuitCmd = ` ${ coindRootDir } / ${ key . toLowerCase ( ) } / ${ shepherd . nativeCoindList [ key . toLowerCase ( ) ] . bin . toLowerCase ( ) } -cli ` ;
}
if ( key === 'CHIPS' ) {
_ coindQuitCmd = chipscliBin ;
}
function execCliStop ( ) {
let _ arg = [ ] ;
if ( chain && ! shepherd . nativeCoindList [ key . toLowerCase ( ) ] && key !== 'CHIPS' ) {
_ arg . push ( ` -ac_name= ${ chain } ` ) ;
}
_ arg . push ( 'stop' ) ;
execFile ( ` ${ _ coindQuitCmd } ` , _ arg , function ( error , stdout , stderr ) {
shepherd . log ( ` stdout: ${ stdout } ` ) ;
shepherd . log ( ` stderr: ${ stderr } ` ) ;
if ( stdout . indexOf ( 'EOF reached' ) > - 1 ||
stderr . indexOf ( 'EOF reached' ) > - 1 ||
( error && error . toString ( ) . indexOf ( 'Command failed' ) > - 1 && ! stderr ) || // win "special snowflake" case
stdout . indexOf ( 'connect to server: unknown (code -1)' ) > - 1 ||
stderr . indexOf ( 'connect to server: unknown (code -1)' ) > - 1 ) {
delete coindInstanceRegistry [ key ] ;
clearInterval ( coindExitInterval [ key ] ) ;
}
// workaround for AGT-65
const _ port = assetChainPorts [ key ] ;
portscanner . checkPortStatus ( _ port , '127.0.0.1' , function ( error , status ) {
// Status is 'open' if currently in use or 'closed' if available
if ( status === 'closed' ) {
delete coindInstanceRegistry [ key ] ;
clearInterval ( coindExitInterval [ key ] ) ;
}
} ) ;
if ( error !== null ) {
shepherd . log ( ` exec error: ${ error } ` ) ;
}
if ( key === 'CHIPS' ) {
setTimeout ( function ( ) {
shepherd . killRogueProcess ( 'chips-cli' ) ;
} , 100 ) ;
} else {
setTimeout ( function ( ) {
shepherd . killRogueProcess ( 'komodo-cli' ) ;
} , 100 ) ;
}
} ) ;
}
execCliStop ( ) ;
coindExitInterval [ key ] = setInterval ( function ( ) {
execCliStop ( ) ;
} , timeout ) ;
}
}
shepherd . getConf = function ( chain ) {
let _ confLocation = chain === 'komodod' ? ` ${ komodoDir } /komodo.conf ` : ` ${ komodoDir } / ${ chain } / ${ chain } .conf ` ;
_ confLocation = chain === 'CHIPS' ? ` ${ chipsDir } /chips.conf ` : _ confLocation ;
// any coind
if ( shepherd . nativeCoindList [ chain . toLowerCase ( ) ] ) {
const _ osHome = os . platform === 'win32' ? process . env . APPDATA : process . env . HOME ;
let coindDebugLogLocation = ` ${ _ osHome } /. ${ shepherd . nativeCoindList [ chain . toLowerCase ( ) ] . bin . toLowerCase ( ) } /debug.log ` ;
_ confLocation = ` ${ _ osHome } /. ${ shepherd . nativeCoindList [ chain . toLowerCase ( ) ] . bin . toLowerCase ( ) } / ${ shepherd . nativeCoindList [ chain . toLowerCase ( ) ] . bin . toLowerCase ( ) } .conf ` ;
}
if ( fs . existsSync ( _ confLocation ) ) {
let _ port = assetChainPorts [ chain ] ;
const _ rpcConf = fs . readFileSync ( _ confLocation , 'utf8' ) ;
// any coind
if ( shepherd . nativeCoindList [ chain . toLowerCase ( ) ] ) {
_ port = shepherd . nativeCoindList [ chain . toLowerCase ( ) ] . port ;
}
if ( _ rpcConf . length ) {
let _ match ;
let parsedRpcConfig = {
user : '' ,
pass : '' ,
port : _ port ,
} ;
if ( _ match = _ rpcConf . match ( /rpcuser=\s*(.*)/ ) ) {
parsedRpcConfig . user = _ match [ 1 ] ;
}
if ( ( _ match = _ rpcConf . match ( /rpcpass=\s*(.*)/ ) ) ||
( _ match = _ rpcConf . match ( /rpcpassword=\s*(.*)/ ) ) ) {
parsedRpcConfig . pass = _ match [ 1 ] ;
}
if ( shepherd . nativeCoindList [ chain . toLowerCase ( ) ] ) {
rpcConf [ chain ] = parsedRpcConfig ;
} else {
rpcConf [ chain === 'komodod' ? 'KMD' : chain ] = parsedRpcConfig ;
}
} else {
shepherd . log ( ` ${ _ confLocation } is empty ` ) ;
}
} else {
shepherd . log ( ` ${ _ confLocation } doesnt exist ` ) ;
}
}
/ *
* type : POST
* params : payload
* /
shepherd . post ( '/cli' , function ( req , res , next ) {
if ( ! req . body . payload ) {
const errorObj = {
msg : 'error' ,
result : 'no payload provided' ,
} ;
res . end ( JSON . stringify ( errorObj ) ) ;
} else if ( ! req . body . payload . cmd . match ( /^[0-9a-zA-Z _\,\.\[\]"'/\\]+$/g ) ) {
const errorObj = {
msg : 'error' ,
result : 'wrong cli string format' ,
} ;
res . end ( JSON . stringify ( errorObj ) ) ;
} else {
const _ mode = req . body . payload . mode === 'passthru' ? 'passthru' : 'default' ;
const _ chain = req . body . payload . chain === 'KMD' ? null : req . body . payload . chain ;
let _ cmd = req . body . payload . cmd ;
const _ params = req . body . payload . params ? ` ${ req . body . payload . params } ` : '' ;
if ( ! rpcConf [ _ chain ] ) {
shepherd . getConf ( req . body . payload . chain === 'KMD' ? 'komodod' : req . body . payload . chain ) ;
}
if ( _ mode === 'default' ) {
/ * l e t _ b o d y = {
agent : 'bitcoinrpc' ,
method : _ cmd ,
} ;
if ( req . body . payload . params ) {
_ body = {
agent : 'bitcoinrpc' ,
method : _ cmd ,
params : req . body . payload . params === ' ' ? [ '' ] : req . body . payload . params ,
} ;
}
const options = {
url : ` http://localhost: ${ rpcConf [ req . body . payload . chain ] . port } ` ,
method : 'POST' ,
auth : {
user : rpcConf [ req . body . payload . chain ] . user ,
pass : rpcConf [ req . body . payload . chain ] . pass ,
} ,
body : JSON . stringify ( _ body )
} ;
// send back body on both success and error
// this bit replicates iguana core's behaviour
request ( options , function ( error , response , body ) {
if ( response &&
response . statusCode &&
response . statusCode === 200 ) {
res . end ( body ) ;
} else {
res . end ( body ) ;
}
} ) ; * /
if ( _ cmd === 'debug' &&
_ chain !== 'CHIPS' ) {
if ( shepherd . nativeCoindList [ _ chain . toLowerCase ( ) ] ) {
const _ osHome = os . platform === 'win32' ? process . env . APPDATA : process . env . HOME ;
let coindDebugLogLocation ;
if ( _ chain === 'CHIPS' ) {
coindDebugLogLocation = ` ${ chipsDir } /debug.log ` ;
} else {
coindDebugLogLocation = ` ${ _ osHome } /. ${ shepherd . nativeCoindList [ _ chain . toLowerCase ( ) ] . bin . toLowerCase ( ) } /debug.log ` ;
}
shepherd . readDebugLog ( coindDebugLogLocation , 1 )
. then ( function ( result ) {
const _ obj = {
'msg' : 'success' ,
'result' : result ,
} ;
// shepherd.log('bitcoinrpc debug ====>');
// console.log(result);
res . end ( JSON . stringify ( _ obj ) ) ;
} , function ( result ) {
const _ obj = {
error : result ,
result : 'error' ,
} ;
res . end ( JSON . stringify ( _ obj ) ) ;
} ) ;
} else {
res . end ( {
error : 'bitcoinrpc debug error' ,
result : 'error' ,
} ) ;
// console.log('bitcoinrpc debug error');
}
} else {
if ( _ chain === 'CHIPS' &&
_ cmd === 'debug' ) {
_ cmd = 'getblockchaininfo' ;
}
let _ body = {
'agent' : 'bitcoinrpc' ,
'method' : _ cmd ,
} ;
if ( req . body . payload . params ) {
_ body = {
'agent' : 'bitcoinrpc' ,
'method' : _ cmd ,
'params' : req . body . payload . params === ' ' ? [ '' ] : req . body . payload . params ,
} ;
}
const options = {
url : ` http://localhost: ${ rpcConf [ req . body . payload . chain ] . port } ` ,
method : 'POST' ,
auth : {
'user' : rpcConf [ req . body . payload . chain ] . user ,
'pass' : rpcConf [ req . body . payload . chain ] . pass
} ,
body : JSON . stringify ( _ body )
} ;
// send back body on both success and error
// this bit replicates iguana core's behaviour
request ( options , function ( error , response , body ) {
if ( response &&
response . statusCode &&
response . statusCode === 200 ) {
res . end ( body ) ;
} else {
res . end ( body ) ;
}
} ) ;
}
} else {
let _ coindCliBin = komodocliBin ;
if ( shepherd . nativeCoindList &&
_ chain &&
shepherd . nativeCoindList [ _ chain . toLowerCase ( ) ] ) {
_ coindCliBin = ` ${ coindRootDir } / ${ _ chain . toLowerCase ( ) } / ${ shepherd . nativeCoindList [ _ chain . toLowerCase ( ) ] . bin . toLowerCase ( ) } -cli ` ;
}
let _ arg = ( _ chain ? ' -ac_name=' + _ chain : '' ) + ' ' + _ cmd + _ params ;
_ arg = _ arg . trim ( ) . split ( ' ' ) ;
execFile ( _ coindCliBin , _ arg , function ( error , stdout , stderr ) {
shepherd . log ( ` stdout: ${ stdout } ` ) ;
shepherd . log ( ` stderr: ${ stderr } ` ) ;
if ( error !== null ) {
shepherd . log ( ` exec error: ${ error } ` ) ;
}
let responseObj ;
if ( stderr ) {
responseObj = {
msg : 'error' ,
result : stderr ,
} ;
} else {
responseObj = {
msg : 'success' ,
result : stdout ,
} ;
}
res . end ( JSON . stringify ( responseObj ) ) ;
shepherd . killRogueProcess ( 'komodo-cli' ) ;
} ) ;
}
}
} ) ;
/ *
* type : POST
* params : payload
* /
shepherd . post ( '/appconf' , function ( req , res , next ) {
if ( ! req . body . payload ) {
const errorObj = {
msg : 'error' ,
result : 'no payload provided' ,
} ;
res . end ( JSON . stringify ( errorObj ) ) ;
} else {
shepherd . saveLocalAppConf ( req . body . payload ) ;
const successObj = {
msg : 'success' ,
result : 'config saved' ,
} ;
res . end ( JSON . stringify ( successObj ) ) ;
}
} ) ;
/ *
* type : POST
* params : none
* /
shepherd . post ( '/appconf/reset' , function ( req , res , next ) {
shepherd . saveLocalAppConf ( shepherd . defaultAppConfig ) ;
const successObj = {
msg : 'success' ,
result : 'config saved' ,
} ;
res . end ( JSON . stringify ( successObj ) ) ;
} ) ;
shepherd . log ( ` agama dir: ${ agamaDir } ` ) ;
shepherd . log ( '--------------------------' )
shepherd . log ( ` komodo dir: ${ komododBin } ` ) ;
shepherd . log ( ` komodo bin: ${ komodoDir } ` ) ;
shepherd . writeLog ( ` agama dir: ${ agamaDir } ` ) ;
shepherd . writeLog ( ` komodo dir: ${ komododBin } ` ) ;
shepherd . writeLog ( ` komodo bin: ${ komodoDir } ` ) ;
// default route
shepherd . get ( '/' , function ( req , res , next ) {
res . send ( 'Agama app server' ) ;
} ) ;
/ *
* type : GET
*
* /
shepherd . get ( '/appconf' , function ( req , res , next ) {
const obj = shepherd . loadLocalConfig ( ) ;
res . send ( obj ) ;
} ) ;
/ *
* type : GET
*
* /
shepherd . get ( '/sysinfo' , function ( req , res , next ) {
const obj = shepherd . SystemInfo ( ) ;
res . send ( obj ) ;
} ) ;
/ *
* type : GET
*
* /
shepherd . get ( '/appinfo' , function ( req , res , next ) {
const obj = shepherd . appInfo ( ) ;
res . send ( obj ) ;
} ) ;
// expose sockets obj
shepherd . setIO = function ( io ) {
shepherd . io = io ;
} ;
shepherd . setVar = function ( _ name , _ body ) {
shepherd [ _ name ] = _ body ;
} ;
/ *
* type : GET
*
* /
shepherd . get ( '/InstantDEX/allcoins' , function ( req , res , next ) {
let successObj ;
let nativeCoindList = [ ] ;
let electrumCoinsList = [ ] ;
for ( let key in electrumCoins ) {
if ( key !== 'auth' ) {
electrumCoinsList . push ( electrumCoins [ key ] . abbr ) ;
}
}
for ( let key in coindInstanceRegistry ) {
nativeCoindList . push ( key === 'komodod' ? 'KMD' : key ) ;
}
successObj = {
native : nativeCoindList ,
spv : electrumCoinsList ,
total : Object . keys ( electrumCoins ) . length - 1 + Object . keys ( nativeCoindList ) . length ,
} ;
res . end ( JSON . stringify ( successObj ) ) ;
} ) ;
/ *
* type : GET
*
* /
shepherd . get ( '/auth/status' , function ( req , res , next ) { // not finished
let successObj ;
let _ status = false ;
if ( Object . keys ( coindInstanceRegistry ) . length ) {
if ( Object . keys ( electrumCoins ) . length > 1 && electrumCoins . auth ) {
_ status = true ;
} else if ( Object . keys ( electrumCoins ) . length === 1 && ! electrumCoins . auth ) {
_ status = true ;
}
} else if ( Object . keys ( electrumCoins ) . length > 1 && electrumCoins . auth ) {
_ status = true ;
} else if ( Object . keys ( electrumCoins ) . length === 1 && ! Object . keys ( coindInstanceRegistry ) . length ) {
_ status = true ;
}
successObj = {
pubkey : 'nativeonly' ,
result : 'success' ,
handle : '' ,
status : _ status ? 'unlocked' : 'locked' ,
duration : 2507830 ,
} ;
res . end ( JSON . stringify ( successObj ) ) ;
} ) ;
/ *
* type : GET
* params : herd , lastLines
* /
shepherd . post ( '/debuglog' , function ( req , res ) {
let _ herd = req . body . herdname ;
let _ ac = req . body . ac ;
let _ lastNLines = req . body . lastLines ;
let _ location ;
if ( os . platform ( ) === 'darwin' ) {
komodoDir = shepherd . appConfig . dataDir . length ? shepherd . appConfig . dataDir : ` ${ process . env . HOME } /Library/Application Support/Komodo ` ;
}
if ( os . platform ( ) === 'linux' ) {
komodoDir = shepherd . appConfig . dataDir . length ? shepherd . appConfig . dataDir : ` ${ process . env . HOME } /.komodo ` ;
}
if ( os . platform ( ) === 'win32' ) {
komodoDir = shepherd . appConfig . dataDir . length ? shepherd . appConfig . dataDir : ` ${ process . env . APPDATA } /Komodo ` ;
komodoDir = path . normalize ( komodoDir ) ;
}
if ( _ herd === 'komodo' ) {
_ location = komodoDir ;
}
if ( _ ac ) {
_ location = ` ${ komodoDir } / ${ _ ac } ` ;
if ( _ ac === 'CHIPS' ) {
_ location = chipsDir ;
}
}
shepherd . readDebugLog ( ` ${ _ location } /debug.log ` , _ lastNLines )
. then ( function ( result ) {
const _ obj = {
msg : 'success' ,
result : result ,
} ;
res . end ( JSON . stringify ( _ obj ) ) ;
} , function ( result ) {
const _ obj = {
msg : 'error' ,
result : result ,
} ;
res . end ( JSON . stringify ( _ obj ) ) ;
} ) ;
} ) ;
/ *
* type : POST
* params : herd
* /
shepherd . post ( '/herd' , function ( req , res ) {
shepherd . log ( '======= req.body =======' ) ;
shepherd . log ( req . body ) ;
if ( req . body . options &&
! shepherd . kmdMainPassiveMode ) {
function testCoindPort ( skipError ) {
if ( ! lockDownAddCoin ) {
const _ port = assetChainPorts [ req . body . options . ac_name ] ;
portscanner . checkPortStatus ( _ port , '127.0.0.1' , function ( error , status ) {
// Status is 'open' if currently in use or 'closed' if available
if ( status === 'open' ) {
if ( ! skipError ) {
shepherd . log ( ` komodod service start error at port ${ _ port } , reason: port is closed ` ) ;
shepherd . writeLog ( ` komodod service start error at port ${ _ port } , reason: port is closed ` ) ;
shepherd . io . emit ( 'service' , {
komodod : {
error : ` error starting ${ req . body . herd } ${ req . body . options . ac_name } daemon. Port ${ _ port } is already taken! ` ,
} ,
} ) ;
const obj = {
msg : 'error' ,
result : ` error starting ${ req . body . herd } ${ req . body . options . ac_name } daemon. Port ${ _ port } is already taken! ` ,
} ;
res . status ( 500 ) ;
res . end ( JSON . stringify ( obj ) ) ;
} else {
shepherd . log ( ` komodod service start success at port ${ _ port } ` ) ;
shepherd . writeLog ( ` komodod service start success at port ${ _ port } ` ) ;
}
} else {
if ( ! skipError ) {
herder ( req . body . herd , req . body . options ) ;
const obj = {
msg : 'success' ,
result : 'result' ,
} ;
res . end ( JSON . stringify ( obj ) ) ;
} else {
shepherd . log ( ` komodod service start error at port ${ _ port } , reason: unknown ` ) ;
shepherd . writeLog ( ` komodod service start error at port ${ _ port } , reason: unknown ` ) ;
}
}
} ) ;
}
}
if ( req . body . herd === 'komodod' ) {
// check if komodod instance is already running
testCoindPort ( ) ;
setTimeout ( function ( ) {
testCoindPort ( true ) ;
} , 10000 ) ;
} else {
herder ( req . body . herd , req . body . options , req . body . coind ) ;
const obj = {
msg : 'success' ,
result : 'result' ,
} ;
res . end ( JSON . stringify ( obj ) ) ;
}
} else {
// (?)
herder ( req . body . herd , req . body . options ) ;
const obj = {
msg : 'success' ,
result : 'result' ,
} ;
res . end ( JSON . stringify ( obj ) ) ;
}
} ) ;
/ *
* type : POST
* /
shepherd . post ( '/setconf' , function ( req , res ) {
shepherd . log ( '======= req.body =======' ) ;
shepherd . log ( req . body ) ;
if ( os . platform ( ) === 'win32' &&
req . body . chain == 'komodod' ) {
setkomodoconf = spawn ( path . join ( __ dirname , '../assets/bin/win64/genkmdconf.bat' ) ) ;
} else {
setConf ( req . body . chain ) ;
}
const obj = {
msg : 'success' ,
result : 'result' ,
} ;
res . end ( JSON . stringify ( obj ) ) ;
} ) ;
/ *
* type : POST
* /
shepherd . post ( '/getconf' , function ( req , res ) {
shepherd . log ( '======= req.body =======' ) ;
shepherd . log ( req . body ) ;
const confpath = getConf ( req . body . chain , req . body . coind ) ;
shepherd . log ( 'got conf path is:' ) ;
shepherd . log ( confpath ) ;
shepherd . writeLog ( 'got conf path is:' ) ;
shepherd . writeLog ( confpath ) ;
const obj = {
msg : 'success' ,
result : confpath ,
} ;
res . end ( JSON . stringify ( obj ) ) ;
} ) ;
/ *
* type : GET
* params : coin , type
* TODO : reorganize to work with coind
* /
shepherd . get ( '/kick' , function ( req , res , next ) {
const _ coin = req . query . coin ;
const _ type = req . query . type ;
if ( ! _ coin ) {
const errorObj = {
msg : 'error' ,
result : 'no coin name provided' ,
} ;
res . end ( JSON . stringify ( errorObj ) ) ;
}
if ( ! _ type ) {
const errorObj = {
msg : 'error' ,
result : 'no type provided' ,
} ;
res . end ( JSON . stringify ( errorObj ) ) ;
}
const kickStartDirs = {
soft : [
{
name : 'DB/[coin]' ,
type : 'pattern' ,
match : 'balancecrc.' ,
} ,
{
name : 'DB/[coin]/utxoaddrs' ,
type : 'file' ,
} ,
{
name : 'DB/[coin]/accounts' ,
type : 'folder' ,
} ,
{
name : 'DB/[coin]/fastfind' ,
type : 'folder' ,
} ,
{
name : 'tmp/[coin]' ,
type : 'folder' ,
}
] ,
hard : [
{
name : 'DB/[coin]' ,
type : 'pattern' ,
match : 'balancecrc.' ,
} ,
{
name : 'DB/[coin]/utxoaddrs' ,
type : 'file' ,
} ,
{
name : 'DB/[coin]' ,
type : 'pattern' ,
match : 'utxoaddrs.' ,
} ,
{
name : 'DB/[coin]/accounts' ,
type : 'folder' ,
} ,
{
name : 'DB/[coin]/fastfind' ,
type : 'folder' ,
} ,
{
name : 'DB/[coin]/spends' ,
type : 'folder' ,
} ,
{
name : 'tmp/[coin]' ,
type : 'folder' ,
}
] ,
brutal : [ // delete all coin related data
{
name : 'DB/[coin]' ,
type : 'folder' ,
} ,
{
name : 'DB/purgeable/[coin]' ,
type : 'folder' ,
} ,
{
name : 'DB/ro/[coin]' ,
type : 'folder' ,
} ,
{
name : 'tmp/[coin]' ,
type : 'folder' ,
}
]
} ;
if ( _ coin &&
_ type ) {
for ( let i = 0 ; i < kickStartDirs [ _ type ] . length ; i ++ ) {
let currentKickItem = kickStartDirs [ _ type ] [ i ] ;
shepherd . log ( 'deleting ' + currentKickItem . type + ( currentKickItem . match ? ' ' + currentKickItem . match : '' ) + ' ' + iguanaDir + '/' + currentKickItem . name . replace ( '[coin]' , _ coin ) ) ;
if ( currentKickItem . type === 'folder' ||
currentKickItem . type === 'file' ) {
/*rimraf(`${iguanaDir}/ $ { currentKickItem . name . replace ( '[coin]' , _ coin ) } ` , function(err) {
if ( err ) {
shepherd . writeLog ( ` kickstart err: ${ err } ` ) ;
shepherd . log ( ` kickstart err: ${ err } ` ) ;
}
} ) ; * /
} else if ( currentKickItem . type === 'pattern' ) {
let dirItems = fs . readdirSync ( ` ${ iguanaDir } /currentKickItem.name.replace('[coin]', _coin) ` ) ;
if ( dirItems &&
dirItems . length ) {
for ( let j = 0 ; j < dirItems . length ; j ++ ) {
if ( dirItems [ j ] . indexOf ( currentKickItem . match ) > - 1 ) {
/*rimraf(`${iguanaDir}/ $ { currentKickItem . name . replace ( '[coin]' , _ coin ) } / $ { dirItems [ j ] } ` , function(err) {
if ( err ) {
shepherd . writeLog ( ` kickstart err: ${ err } ` ) ;
shepherd . log ( ` kickstart err: ${ err } ` ) ;
}
} ) ; * /
shepherd . log ( ` deleting ${ dirItems [ j ] } ` ) ;
}
}
}
}
}
const successObj = {
msg : 'success' ,
result : 'kickstart: brutal is executed' ,
} ;
res . end ( JSON . stringify ( successObj ) ) ;
}
} ) ;
shepherd . readDebugLog = function ( fileLocation , lastNLines ) {
return new Promise (
function ( resolve , reject ) {
if ( lastNLines ) {
try {
_ fs . access ( fileLocation , fs . constants . R_OK , function ( err ) {
if ( err ) {
shepherd . log ( ` error reading ${ fileLocation } ` ) ;
shepherd . writeLog ( ` error reading ${ fileLocation } ` ) ;
reject ( ` readDebugLog error: ${ err } ` ) ;
} else {
shepherd . log ( ` reading ${ fileLocation } ` ) ;
_ fs . readFile ( fileLocation , 'utf-8' , function ( err , data ) {
if ( err ) {
shepherd . writeLog ( ` readDebugLog err: ${ err } ` ) ;
shepherd . log ( ` readDebugLog err: ${ err } ` ) ;
}
const lines = data . trim ( ) . split ( '\n' ) ;
const lastLine = lines . slice ( lines . length - lastNLines , lines . length ) . join ( '\n' ) ;
resolve ( lastLine ) ;
} ) ;
}
} ) ;
} catch ( e ) {
reject ( ` readDebugLog error: ${ e } ` ) ;
}
} else {
reject ( 'readDebugLog error: lastNLines param is not provided!' ) ;
}
}
) ;
} ;
function herder ( flock , data , coind ) {
if ( data === undefined ) {
data = 'none' ;
shepherd . log ( 'it is undefined' ) ;
}
shepherd . log ( 'herder ' + flock + ' ' + coind ) ;
shepherd . log ( ` selected data: ${ JSON . stringify ( data , null , '\t' ) } ` ) ;
// TODO: notify gui that reindex/rescan param is used to reflect on the screen
// asset chain debug.log unlink
if ( flock === 'komodod' ) {
let kmdDebugLogLocation = ( data . ac_name !== 'komodod' ? komodoDir + '/' + data . ac_name : komodoDir ) + '/debug.log' ;
shepherd . log ( 'komodod flock selected...' ) ;
shepherd . log ( 'selected data: ' + JSON . stringify ( data , null , '\t' ) ) ;
shepherd . writeLog ( 'komodod flock selected...' ) ;
shepherd . writeLog ( ` selected data: ${ data } ` ) ;
// truncate debug.log
if ( ! shepherd . kmdMainPassiveMode ) {
try {
const _ confFileAccess = _ fs . accessSync ( kmdDebugLogLocation , fs . R_OK | fs . W_OK ) ;
if ( _ confFileAccess ) {
shepherd . log ( ` error accessing ${ kmdDebugLogLocation } ` ) ;
shepherd . writeLog ( ` error accessing ${ kmdDebugLogLocation } ` ) ;
} else {
try {
fs . unlinkSync ( kmdDebugLogLocation ) ;
shepherd . log ( ` truncate ${ kmdDebugLogLocation } ` ) ;
shepherd . writeLog ( ` truncate ${ kmdDebugLogLocation } ` ) ;
} catch ( e ) {
shepherd . log ( 'cant unlink debug.log' ) ;
}
}
} catch ( e ) {
shepherd . log ( ` komodod debug.log access err: ${ e } ` ) ;
shepherd . writeLog ( ` komodod debug.log access err: ${ e } ` ) ;
}
}
// get komodod instance port
const _ port = assetChainPorts [ data . ac_name ] ;
try {
// check if komodod instance is already running
portscanner . checkPortStatus ( _ port , '127.0.0.1' , function ( error , status ) {
// Status is 'open' if currently in use or 'closed' if available
if ( status === 'closed' ) {
// start komodod via exec
const _ customParamDict = {
silent : '&' ,
reindex : '-reindex' ,
change : '-pubkey=' ,
datadir : '-datadir=' ,
rescan : '-rescan' ,
} ;
let _ customParam = '' ;
if ( data . ac_custom_param === 'silent' ||
data . ac_custom_param === 'reindex' ||
data . ac_custom_param === 'rescan' ) {
_ customParam = ` ${ _ customParamDict [ data . ac_custom_param ] } ` ;
} else if ( data . ac_custom_param === 'change' && data . ac_custom_param_value ) {
_ customParam = ` ${ _ customParamDict [ data . ac_custom_param ] } ${ data . ac_custom_param_value } ` ;
}
if ( shepherd . appConfig . dataDir . length ) {
_ customParam = _ customParam + ' -datadir=' + shepherd . appConfig . dataDir + '/' + data . ac_name ;
}
shepherd . log ( ` exec ${ komododBin } ${ data . ac_options . join ( ' ' ) } ${ _ customParam } ` ) ;
shepherd . writeLog ( ` exec ${ komododBin } ${ data . ac_options . join ( ' ' ) } ${ _ customParam } ` ) ;
const isChain = data . ac_name . match ( /^[A-Z]*$/ ) ;
const coindACParam = isChain ? ` -ac_name= ${ data . ac_name } ` : '' ;
shepherd . log ( ` daemon param ${ data . ac_custom_param } ` ) ;
coindInstanceRegistry [ data . ac_name ] = true ;
if ( ! shepherd . kmdMainPassiveMode ) {
let _ arg = ` ${ coindACParam } ${ data . ac_options . join ( ' ' ) } ${ _ customParam } ` ;
_ arg = _ arg . trim ( ) . split ( ' ' ) ;
execFile ( ` ${ komododBin } ` , _ arg , {
maxBuffer : 1024 * 1000000 // 1000 mb
} , function ( error , stdout , stderr ) {
shepherd . writeLog ( ` stdout: ${ stdout } ` ) ;
shepherd . writeLog ( ` stderr: ${ stderr } ` ) ;
if ( error !== null ) {
shepherd . log ( ` exec error: ${ error } ` ) ;
shepherd . writeLog ( ` exec error: ${ error } ` ) ;
if ( error . toString ( ) . indexOf ( 'using -reindex' ) > - 1 ) {
shepherd . io . emit ( 'service' , {
komodod : {
error : 'run -reindex' ,
} ,
} ) ;
}
}
} ) ;
}
} else {
if ( shepherd . kmdMainPassiveMode ) {
coindInstanceRegistry [ data . ac_name ] = true ;
}
shepherd . log ( ` port ${ _ port } ( ${ data . ac_name } ) is already in use ` ) ;
shepherd . writeLog ( ` port ${ _ port } ( ${ data . ac_name } ) is already in use ` ) ;
}
} ) ;
} catch ( e ) {
shepherd . log ( ` failed to start komodod err: ${ e } ` ) ;
shepherd . writeLog ( ` failed to start komodod err: ${ e } ` ) ;
}
}
if ( flock === 'chipsd' ) {
let kmdDebugLogLocation = chipsDir + '/debug.log' ;
shepherd . log ( 'chipsd flock selected...' ) ;
shepherd . log ( 'selected data: ' + JSON . stringify ( data , null , '\t' ) ) ;
shepherd . writeLog ( 'chipsd flock selected...' ) ;
shepherd . writeLog ( ` selected data: ${ data } ` ) ;
// truncate debug.log
try {
const _ confFileAccess = _ fs . accessSync ( kmdDebugLogLocation , fs . R_OK | fs . W_OK ) ;
if ( _ confFileAccess ) {
shepherd . log ( ` error accessing ${ kmdDebugLogLocation } ` ) ;
shepherd . writeLog ( ` error accessing ${ kmdDebugLogLocation } ` ) ;
} else {
try {
fs . unlinkSync ( kmdDebugLogLocation ) ;
shepherd . log ( ` truncate ${ kmdDebugLogLocation } ` ) ;
shepherd . writeLog ( ` truncate ${ kmdDebugLogLocation } ` ) ;
} catch ( e ) {
shepherd . log ( 'cant unlink debug.log' ) ;
}
}
} catch ( e ) {
shepherd . log ( ` chipsd debug.log access err: ${ e } ` ) ;
shepherd . writeLog ( ` chipsd debug.log access err: ${ e } ` ) ;
}
// get komodod instance port
const _ port = assetChainPorts . chipsd ;
try {
// check if komodod instance is already running
portscanner . checkPortStatus ( _ port , '127.0.0.1' , function ( error , status ) {
// Status is 'open' if currently in use or 'closed' if available
if ( status === 'closed' ) {
// start komodod via exec
const _ customParamDict = {
silent : '&' ,
reindex : '-reindex' ,
change : '-pubkey=' ,
rescan : '-rescan' ,
} ;
let _ customParam = '' ;
if ( data . ac_custom_param === 'silent' ||
data . ac_custom_param === 'reindex' ||
data . ac_custom_param === 'rescan' ) {
_ customParam = ` ${ _ customParamDict [ data . ac_custom_param ] } ` ;
} else if ( data . ac_custom_param === 'change' && data . ac_custom_param_value ) {
_ customParam = ` ${ _ customParamDict [ data . ac_custom_param ] } ${ data . ac_custom_param_value } ` ;
}
shepherd . log ( ` exec ${ chipsBin } ${ _ customParam } ` ) ;
shepherd . writeLog ( ` exec ${ chipsBin } ${ _ customParam } ` ) ;
shepherd . log ( ` daemon param ${ data . ac_custom_param } ` ) ;
coindInstanceRegistry [ 'CHIPS' ] = true ;
let _ arg = ` ${ _ customParam } ` ;
_ arg = _ arg . trim ( ) . split ( ' ' ) ;
if ( _ arg &&
_ arg . length > 1 ) {
execFile ( ` ${ chipsBin } ` , _ arg , {
maxBuffer : 1024 * 1000000 // 1000 mb
} , function ( error , stdout , stderr ) {
shepherd . writeLog ( ` stdout: ${ stdout } ` ) ;
shepherd . writeLog ( ` stderr: ${ stderr } ` ) ;
if ( error !== null ) {
shepherd . log ( ` exec error: ${ error } ` ) ;
shepherd . writeLog ( ` exec error: ${ error } ` ) ;
if ( error . toString ( ) . indexOf ( 'using -reindex' ) > - 1 ) {
shepherd . io . emit ( 'service' , {
komodod : {
error : 'run -reindex' ,
} ,
} ) ;
}
}
} ) ;
} else {
execFile ( ` ${ chipsBin } ` , {
maxBuffer : 1024 * 1000000 // 1000 mb
} , function ( error , stdout , stderr ) {
shepherd . writeLog ( ` stdout: ${ stdout } ` ) ;
shepherd . writeLog ( ` stderr: ${ stderr } ` ) ;
if ( error !== null ) {
shepherd . log ( ` exec error: ${ error } ` ) ;
shepherd . writeLog ( ` exec error: ${ error } ` ) ;
if ( error . toString ( ) . indexOf ( 'using -reindex' ) > - 1 ) {
shepherd . io . emit ( 'service' , {
komodod : {
error : 'run -reindex' ,
} ,
} ) ;
}
}
} ) ;
}
}
} ) ;
} catch ( e ) {
shepherd . log ( ` failed to start chipsd err: ${ e } ` ) ;
shepherd . writeLog ( ` failed to start chipsd err: ${ e } ` ) ;
}
}
if ( flock === 'zcashd' ) { // TODO: fix(?)
let kmdDebugLogLocation = ` ${ zcashDir } /debug.log ` ;
shepherd . log ( 'zcashd flock selected...' ) ;
shepherd . log ( ` selected data: ${ data } ` ) ;
shepherd . writeLog ( 'zcashd flock selected...' ) ;
shepherd . writeLog ( ` selected data: ${ data } ` ) ;
/*pm2.connect(true, function(err) { / / start up pm2 god
if ( err ) {
shepherd . error ( err ) ;
process . exit ( 2 ) ;
}
pm2 . start ( {
script : zcashdBin , // path to binary
name : data . ac_name , // REVS, USD, EUR etc.
exec_mode : 'fork' ,
cwd : zcashDir ,
args : data . ac_options
} , function ( err , apps ) {
shepherd . writeLog ( ` zcashd fork started ${ data . ac_name } ${ JSON . stringify ( data . ac_options ) } ` ) ;
pm2 . disconnect ( ) ; // Disconnect from PM2
if ( err ) {
shepherd . writeLog ( ` pm2.disconnect err: ${ err } ` ) ;
shepherd . log ( ` pm2.disconnect err: ${ err } ` ) ;
}
// throw err;
} ) ;
} ) ; * /
}
if ( flock === 'coind' ) {
const _ osHome = os . platform === 'win32' ? process . env . APPDATA : process . env . HOME ;
let coindDebugLogLocation = ` ${ _ osHome } /. ${ shepherd . nativeCoindList [ coind . toLowerCase ( ) ] . bin . toLowerCase ( ) } /debug.log ` ;
shepherd . log ( ` coind ${ coind } flock selected... ` ) ;
shepherd . log ( ` selected data: ${ JSON . stringify ( data , null , '\t' ) } ` ) ;
shepherd . writeLog ( ` coind ${ coind } flock selected... ` ) ;
shepherd . writeLog ( ` selected data: ${ data } ` ) ;
// truncate debug.log
try {
_ fs . access ( coindDebugLogLocation , fs . constants . R_OK , function ( err ) {
if ( err ) {
shepherd . log ( ` error accessing ${ coindDebugLogLocation } ` ) ;
shepherd . writeLog ( ` error accessing ${ coindDebugLogLocation } ` ) ;
} else {
shepherd . log ( ` truncate ${ coindDebugLogLocation } ` ) ;
shepherd . writeLog ( ` truncate ${ coindDebugLogLocation } ` ) ;
fs . unlink ( coindDebugLogLocation ) ;
}
} ) ;
} catch ( e ) {
shepherd . log ( ` coind ${ coind } debug.log access err: ${ e } ` ) ;
shepherd . writeLog ( ` coind ${ coind } debug.log access err: ${ e } ` ) ;
}
// get komodod instance port
const _ port = shepherd . nativeCoindList [ coind . toLowerCase ( ) ] . port ;
const coindBin = ` ${ coindRootDir } / ${ coind . toLowerCase ( ) } / ${ shepherd . nativeCoindList [ coind . toLowerCase ( ) ] . bin . toLowerCase ( ) } d ` ;
try {
// check if coind instance is already running
portscanner . checkPortStatus ( _ port , '127.0.0.1' , function ( error , status ) {
// Status is 'open' if currently in use or 'closed' if available
if ( status === 'closed' ) {
shepherd . log ( ` exec ${ coindBin } ${ data . ac_options . join ( ' ' ) } ` ) ;
shepherd . writeLog ( ` exec ${ coindBin } ${ data . ac_options . join ( ' ' ) } ` ) ;
coindInstanceRegistry [ coind ] = true ;
let _ arg = ` ${ data . ac_options . join ( ' ' ) } ` ;
_ arg = _ arg . trim ( ) . split ( ' ' ) ;
execFile ( ` ${ coindBin } ` , _ arg , {
maxBuffer : 1024 * 1000000 // 1000 mb
} , function ( error , stdout , stderr ) {
shepherd . writeLog ( ` stdout: ${ stdout } ` ) ;
shepherd . writeLog ( ` stderr: ${ stderr } ` ) ;
if ( error !== null ) {
shepherd . log ( ` exec error: ${ error } ` ) ;
shepherd . writeLog ( ` exec error: ${ error } ` ) ;
}
} ) ;
} else {
shepherd . log ( ` port ${ _ port } ( ${ coind } ) is already in use ` ) ;
shepherd . writeLog ( ` port ${ _ port } ( ${ coind } ) is already in use ` ) ;
}
} ) ;
} catch ( e ) {
shepherd . log ( ` failed to start ${ coind } err: ${ e } ` ) ;
shepherd . writeLog ( ` failed to start ${ coind } err: ${ e } ` ) ;
}
}
}
shepherd . setConfKMD = function ( isChips ) {
// check if kmd conf exists
_ fs . access ( isChips ? ` ${ chipsDir } /chips.conf ` : ` ${ komodoDir } /komodo.conf ` , fs . constants . R_OK , function ( err ) {
if ( err ) {
shepherd . log ( isChips ? 'creating chips conf' : 'creating komodo conf' ) ;
shepherd . writeLog ( isChips ? ` creating chips conf in ${ chipsDir } /chips.conf ` : ` creating komodo conf in ${ komodoDir } /komodo.conf ` ) ;
setConf ( isChips ? 'chipsd' : 'komodod' ) ;
} else {
const _ confSize = fs . lstatSync ( isChips ? ` ${ chipsDir } /chips.conf ` : ` ${ komodoDir } /komodo.conf ` ) ;
if ( _ confSize . size === 0 ) {
shepherd . log ( isChips ? 'err: chips conf file is empty, creating chips conf' : 'err: komodo conf file is empty, creating komodo conf' ) ;
shepherd . writeLog ( isChips ? ` creating chips conf in ${ chipsDir } /chips.conf ` : ` creating komodo conf in ${ komodoDir } /komodo.conf ` ) ;
setConf ( isChips ? 'chipsd' : 'komodod' ) ;
} else {
shepherd . writeLog ( isChips ? 'chips conf exists' : 'komodo conf exists' ) ;
shepherd . log ( isChips ? 'chips conf exists' : 'komodo conf exists' ) ;
}
}
} ) ;
}
function setConf ( flock , coind ) {
let nativeCoindDir ;
let DaemonConfPath ;
shepherd . log ( flock ) ;
shepherd . writeLog ( ` setconf ${ flock } ` ) ;
if ( os . platform ( ) === 'darwin' ) {
nativeCoindDir = coind ? ` ${ process . env . HOME } /Library/Application Support/ ${ shepherd . nativeCoindList [ coind . toLowerCase ( ) ] . bin } ` : null ;
}
if ( os . platform ( ) === 'linux' ) {
nativeCoindDir = coind ? ` ${ process . env . HOME } /. ${ shepherd . nativeCoindList [ coind . toLowerCase ( ) ] . bin . toLowerCase ( ) } ` : null ;
}
if ( os . platform ( ) === 'win32' ) {
nativeCoindDir = coind ? ` ${ process . env . APPDATA } / ${ shepherd . nativeCoindList [ coind . toLowerCase ( ) ] . bin } ` : null ;
}
switch ( flock ) {
case 'komodod' :
DaemonConfPath = ` ${ komodoDir } /komodo.conf ` ;
if ( os . platform ( ) === 'win32' ) {
DaemonConfPath = path . normalize ( DaemonConfPath ) ;
}
break ;
case 'zcashd' :
DaemonConfPath = ` ${ ZcashDir } /zcash.conf ` ;
if ( os . platform ( ) === 'win32' ) {
DaemonConfPath = path . normalize ( DaemonConfPath ) ;
}
break ;
case 'chipsd' :
DaemonConfPath = ` ${ chipsDir } /chips.conf ` ;
if ( os . platform ( ) === 'win32' ) {
DaemonConfPath = path . normalize ( DaemonConfPath ) ;
}
break ;
case 'coind' :
DaemonConfPath = ` ${ nativeCoindDir } / ${ shepherd . nativeCoindList [ coind . toLowerCase ( ) ] . bin . toLowerCase ( ) } .conf ` ;
if ( os . platform ( ) === 'win32' ) {
DaemonConfPath = path . normalize ( DaemonConfPath ) ;
}
break ;
default :
DaemonConfPath = ` ${ komodoDir } / ${ flock } / ${ flock } .conf ` ;
if ( os . platform ( ) === 'win32' ) {
DaemonConfPath = path . normalize ( DaemonConfPath ) ;
}
}
shepherd . log ( DaemonConfPath ) ;
shepherd . writeLog ( ` setconf ${ DaemonConfPath } ` ) ;
const CheckFileExists = function ( ) {
return new Promise ( function ( resolve , reject ) {
const result = 'Check Conf file exists is done' ;
const confFileExist = fs . ensureFileSync ( DaemonConfPath ) ;
if ( confFileExist ) {
shepherd . log ( result ) ;
shepherd . writeLog ( ` setconf ${ result } ` ) ;
resolve ( result ) ;
} else {
shepherd . log ( 'conf file doesnt exist' ) ;
resolve ( 'conf file doesnt exist' ) ;
}
} ) ;
}
const FixFilePermissions = function ( ) {
return new Promise ( function ( resolve , reject ) {
const result = 'Conf file permissions updated to Read/Write' ;
fsnode . chmodSync ( DaemonConfPath , '0666' ) ;
shepherd . log ( result ) ;
shepherd . writeLog ( ` setconf ${ result } ` ) ;
resolve ( result ) ;
} ) ;
}
const RemoveLines = function ( ) {
return new Promise ( function ( resolve , reject ) {
const result = 'RemoveLines is done' ;
fs . readFile ( DaemonConfPath , 'utf8' , function ( err , data ) {
if ( err ) {
shepherd . writeLog ( ` setconf error ${ err } ` ) ;
return shepherd . log ( err ) ;
}
const rmlines = data . replace ( /(?:(?:\r\n|\r|\n)\s*){2}/gm , '\n' ) ;
fs . writeFile ( DaemonConfPath , rmlines , 'utf8' , function ( err ) {
if ( err )
return shepherd . log ( err ) ;
fsnode . chmodSync ( DaemonConfPath , '0666' ) ;
shepherd . writeLog ( ` setconf ${ result } ` ) ;
shepherd . log ( result ) ;
resolve ( result ) ;
} ) ;
} ) ;
} ) ;
}
const CheckConf = function ( ) {
return new Promise ( function ( resolve , reject ) {
const result = 'CheckConf is done' ;
setconf . status ( DaemonConfPath , function ( err , status ) {
const rpcuser = function ( ) {
return new Promise ( function ( resolve , reject ) {
const result = 'checking rpcuser...' ;
if ( status [ 0 ] . hasOwnProperty ( 'rpcuser' ) ) {
shepherd . log ( 'rpcuser: OK' ) ;
shepherd . writeLog ( 'rpcuser: OK' ) ;
} else {
const randomstring = md5 ( ( Math . random ( ) * Math . random ( ) * 999 ) . toString ( ) ) ;
shepherd . log ( 'rpcuser: NOT FOUND' ) ;
shepherd . writeLog ( 'rpcuser: NOT FOUND' ) ;
fs . appendFile ( DaemonConfPath , ` \n rpcuser=user ${ randomstring . substring ( 0 , 16 ) } ` , ( err ) => {
if ( err ) {
shepherd . writeLog ( ` append daemon conf err: ${ err } ` ) ;
shepherd . log ( ` append daemon conf err: ${ err } ` ) ;
}
// throw err;
shepherd . log ( 'rpcuser: ADDED' ) ;
shepherd . writeLog ( 'rpcuser: ADDED' ) ;
} ) ;
}
resolve ( result ) ;
} ) ;
}
const rpcpass = function ( ) {
return new Promise ( function ( resolve , reject ) {
const result = 'checking rpcpassword...' ;
if ( status [ 0 ] . hasOwnProperty ( 'rpcpassword' ) ) {
shepherd . log ( 'rpcpassword: OK' ) ;
shepherd . writeLog ( 'rpcpassword: OK' ) ;
} else {
var randomstring = md5 ( ( Math . random ( ) * Math . random ( ) * 999 ) . toString ( ) ) ;
shepherd . log ( 'rpcpassword: NOT FOUND' ) ;
shepherd . writeLog ( 'rpcpassword: NOT FOUND' ) ;
fs . appendFile ( DaemonConfPath , ` \n rpcpassword= ${ randomstring } ` , ( err ) => {
if ( err ) {
shepherd . writeLog ( ` append daemon conf err: ${ err } ` ) ;
shepherd . log ( ` append daemon conf err: ${ err } ` ) ;
}
// throw err;
shepherd . log ( 'rpcpassword: ADDED' ) ;
shepherd . writeLog ( 'rpcpassword: ADDED' ) ;
} ) ;
}
resolve ( result ) ;
} ) ;
}
const rpcbind = function ( ) {
return new Promise ( function ( resolve , reject ) {
const result = 'checking rpcbind...' ;
if ( status [ 0 ] . hasOwnProperty ( 'rpcbind' ) ) {
shepherd . log ( 'rpcbind: OK' ) ;
shepherd . writeLog ( 'rpcbind: OK' ) ;
} else {
shepherd . log ( 'rpcbind: NOT FOUND' ) ;
shepherd . writeLog ( 'rpcbind: NOT FOUND' ) ;
fs . appendFile ( DaemonConfPath , '\nrpcbind=127.0.0.1' , ( err ) => {
if ( err ) {
shepherd . writeLog ( ` append daemon conf err: ${ err } ` ) ;
shepherd . log ( ` append daemon conf err: ${ err } ` ) ;
}
// throw err;
shepherd . log ( 'rpcbind: ADDED' ) ;
shepherd . writeLog ( 'rpcbind: ADDED' ) ;
} ) ;
}
resolve ( result ) ;
} ) ;
}
const server = function ( ) {
return new Promise ( function ( resolve , reject ) {
const result = 'checking server...' ;
if ( status [ 0 ] . hasOwnProperty ( 'server' ) ) {
shepherd . log ( 'server: OK' ) ;
shepherd . writeLog ( 'server: OK' ) ;
} else {
shepherd . log ( 'server: NOT FOUND' ) ;
shepherd . writeLog ( 'server: NOT FOUND' ) ;
fs . appendFile ( DaemonConfPath , '\nserver=1' , ( err ) => {
if ( err ) {
shepherd . writeLog ( ` append daemon conf err: ${ err } ` ) ;
shepherd . log ( ` append daemon conf err: ${ err } ` ) ;
}
// throw err;
shepherd . log ( 'server: ADDED' ) ;
shepherd . writeLog ( 'server: ADDED' ) ;
} ) ;
}
resolve ( result ) ;
} ) ;
}
const addnode = function ( ) {
return new Promise ( function ( resolve , reject ) {
const result = 'checking addnode...' ;
if ( flock === 'chipsd' ||
flock === 'komodod' ) {
if ( status [ 0 ] . hasOwnProperty ( 'addnode' ) ) {
shepherd . log ( 'addnode: OK' ) ;
shepherd . writeLog ( 'addnode: OK' ) ;
} else {
let nodesList ;
if ( flock === 'chipsd' ) {
nodesList = '\naddnode=95.110.191.193' +
'\naddnode=144.76.167.66' +
'\naddnode=158.69.248.93' +
'\naddnode=149.202.49.218' +
'\naddnode=95.213.205.222' +
'\naddnode=5.9.253.198' +
'\naddnode=164.132.224.253' +
'\naddnode=163.172.4.66' +
'\naddnode=217.182.194.216' +
'\naddnode=94.130.96.114' +
'\naddnode=5.9.253.195' ;
} else if ( flock === 'komodod' ) {
nodesList = '\naddnode=78.47.196.146' +
'\naddnode=5.9.102.210' +
'\naddnode=178.63.69.164' +
'\naddnode=88.198.65.74' +
'\naddnode=5.9.122.241' +
'\naddnode=144.76.94.3' ;
}
shepherd . log ( 'addnode: NOT FOUND' ) ;
fs . appendFile ( DaemonConfPath , nodesList , ( err ) => {
if ( err ) {
shepherd . writeLog ( ` append daemon conf err: ${ err } ` ) ;
shepherd . log ( ` append daemon conf err: ${ err } ` ) ;
}
// throw err;
shepherd . log ( 'addnode: ADDED' ) ;
shepherd . writeLog ( 'addnode: ADDED' ) ;
} ) ;
}
} else {
result = 'skip addnode' ;
}
resolve ( result ) ;
} ) ;
}
rpcuser ( )
. then ( function ( result ) {
return rpcpass ( ) ;
} )
. then ( server )
. then ( rpcbind )
. then ( addnode ) ;
} ) ;
shepherd . log ( result ) ;
shepherd . writeLog ( ` checkconf addnode ${ result } ` ) ;
resolve ( result ) ;
} ) ;
}
CheckFileExists ( )
. then ( function ( result ) {
return FixFilePermissions ( ) ;
} )
. then ( RemoveLines )
. then ( CheckConf ) ;
}
function getConf ( flock , coind ) {
let DaemonConfPath = '' ;
let nativeCoindDir ;
if ( flock === 'CHIPS' ) {
flock = 'chipsd' ;
}
shepherd . log ( flock ) ;
shepherd . log ( ` getconf coind ${ coind } ` ) ;
shepherd . writeLog ( ` getconf flock: ${ flock } ` ) ;
if ( os . platform ( ) === 'darwin' ) {
nativeCoindDir = ` ${ process . env . HOME } /Library/Application Support/ ${ shepherd . nativeCoindList [ coind . toLowerCase ( ) ] . bin } ` ;
}
if ( os . platform ( ) === 'linux' ) {
nativeCoindDir = coind ? ` ${ process . env . HOME } /. ${ shepherd . nativeCoindList [ coind . toLowerCase ( ) ] . bin . toLowerCase ( ) } ` : null ;
}
if ( os . platform ( ) === 'win32' ) {
nativeCoindDir = coind ? ` ${ process . env . APPDATA } / ${ shepherd . nativeCoindList [ coind . toLowerCase ( ) ] . bin } ` : null ;
}
switch ( flock ) {
case 'komodod' :
DaemonConfPath = komodoDir ;
if ( os . platform ( ) === 'win32' ) {
DaemonConfPath = path . normalize ( DaemonConfPath ) ;
shepherd . log ( '===>>> SHEPHERD API OUTPUT ===>>>' ) ;
}
break ;
case 'zcashd' :
DaemonConfPath = ZcashDir ;
if ( os . platform ( ) === 'win32' ) {
DaemonConfPath = path . normalize ( DaemonConfPath ) ;
}
break ;
case 'chipsd' :
DaemonConfPath = chipsDir ;
if ( os . platform ( ) === 'win32' ) {
DaemonConfPath = path . normalize ( DaemonConfPath ) ;
}
break ;
case 'coind' :
DaemonConfPath = os . platform ( ) === 'win32' ? path . normalize ( ` ${ coindRootDir } / ${ coind . toLowerCase ( ) } ` ) : ` ${ coindRootDir } / ${ coind . toLowerCase ( ) } ` ;
break ;
default :
DaemonConfPath = ` ${ komodoDir } / ${ flock } ` ;
if ( os . platform ( ) === 'win32' ) {
DaemonConfPath = path . normalize ( DaemonConfPath ) ;
}
}
shepherd . writeLog ( ` getconf path: ${ DaemonConfPath } ` ) ;
shepherd . log ( ` daemon path: ${ DaemonConfPath } ` ) ;
return DaemonConfPath ;
}
function formatBytes ( bytes , decimals ) {
if ( bytes === 0 )
return '0 Bytes' ;
const k = 1000 ;
const dm = ( decimals + 1 ) || 3 ;
const sizes = [
'Bytes' ,
'KB' ,
'MB' ,
'GB' ,
'TB' ,
'PB' ,
'EB' ,
'ZB' ,
'YB'
] ;
const i = Math . floor ( Math . log ( bytes ) / Math . log ( k ) ) ;
return ` ${ parseFloat ( ( bytes / Math . pow ( k , i ) ) . toFixed ( dm ) ) } ${ sizes [ i ] } ` ;
}
shepherd . SystemInfo = function ( ) {
const os_data = {
'totalmem_bytes' : os . totalmem ( ) ,
'totalmem_readable' : formatBytes ( os . totalmem ( ) ) ,
'arch' : os . arch ( ) ,
'cpu' : os . cpus ( ) [ 0 ] . model ,
'cpu_cores' : os . cpus ( ) . length ,
'platform' : os . platform ( ) ,
'os_release' : os . release ( ) ,
'os_type' : os . type ( ) ,
} ;
return os_data ;
}
shepherd . appInfo = function ( ) {
const sysInfo = shepherd . SystemInfo ( ) ;
const releaseInfo = shepherd . appBasicInfo ;
const dirs = {
agamaDir ,
komodoDir ,
komododBin ,
configLocation : ` ${ agamaDir } /config.json ` ,
cacheLocation : ` ${ agamaDir } /shepherd ` ,
} ;
return {
sysInfo ,
releaseInfo ,
dirs ,
appSession : shepherd . appSessionHash ,
} ;
}
module . exports = shepherd ;