From 7e6b34db29da282f5eb49d0d72b8e7e12f8e6b28 Mon Sep 17 00:00:00 2001 From: pbca26 Date: Tue, 17 Oct 2017 22:21:47 +0300 Subject: [PATCH 1/3] assing all to shepherd obj --- routes/shepherd.js | 1297 +++++++++++++++++--------------------------- 1 file changed, 494 insertions(+), 803 deletions(-) diff --git a/routes/shepherd.js b/routes/shepherd.js index 538e96e..5926c2d 100644 --- a/routes/shepherd.js +++ b/routes/shepherd.js @@ -1,210 +1,52 @@ 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 appRuntimeSPVLog = []; -var lockDownAddCoin = false; -var electrumCoins = { +const app = electron.app; +let shepherd = express.Router(); + +shepherd.path = require('path'); +shepherd.os = require('os'); +shepherd.fsnode = require('fs'); +shepherd.fs = require('fs-extra'); +shepherd._fs = require('graceful-fs'); +// shepherd.md5 = require('./md5.js'); +shepherd.request = require('request'); +shepherd.portscanner = require('portscanner'); +shepherd.aes256 = require('nodejs-aes256'); +shepherd.AdmZip = require('adm-zip'); +shepherd.remoteFileSize = require('remote-file-size'); +shepherd.Promise = require('bluebird'); +shepherd.exec = require('child_process').exec; +shepherd.execFile = require('child_process').execFile; +shepherd.sha256 = require('sha256'); +shepherd.CoinKey = require('coinkey'); +shepherd.bitcoinJS = require('bitcoinjs-lib'); +shepherd.coinSelect = require('coinselect'); +shepherd.fixPath = require('fix-path'); +shepherd.crypto = require('crypto'); + +shepherd.setconf = require('../private/setconf.js'); +shepherd.nativeCoind = require('./nativeCoind.js'); +shepherd.nativeCoindList = {}; +shepherd.assetChainPorts = require('./ports.js'); +shepherd._appConfig = require('./appConfig.js'); + +shepherd.coindInstanceRegistry = {}; +shepherd.guiLog = {}; +shepherd.rpcConf = {}; +shepherd.appRuntimeLog = []; +shepherd.appRuntimeSPVLog = []; +shepherd.lockDownAddCoin = false; +shepherd.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 = { - /*zcash: { - 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' - ], - }, -}; +shepherd.electrumKeys = {}; -const zcashParamsDownloadLinks = { +shepherd.electrumJSCore = require('./electrumjs/electrumjs.core.js'); +shepherd.electrumJSNetworks = require('./electrumjs/electrumjs.networks.js'); +shepherd.electrumJSTxDecoder = require('./electrumjs/electrumjs.txdecoder.js'); +shepherd.electrumServers = require('./electrumjs/electrumServers.js'); + +shepherd.zcashParamsDownloadLinks = { 'agama.komodoplatform.com': { proving: 'https://agama.komodoplatform.com/file/supernet/sprout-proving.key', verifying: 'https://agama.komodoplatform.com/file/supernet/sprout-verifying.key', @@ -219,237 +61,85 @@ const zcashParamsDownloadLinks = { }, }; -shepherd.zcashParamsDownloadLinks = zcashParamsDownloadLinks; - -const CONNECTION_ERROR_OR_INCOMPLETE_DATA = 'connection error or incomplete data'; +shepherd.CONNECTION_ERROR_OR_INCOMPLETE_DATA = 'connection error or incomplete data'; -shepherd.appConfig = _appConfig.config; +shepherd.appConfig = shepherd._appConfig.config; -var agamaDir; -switch (os.platform()) { +shepherd.agamaDir; +switch (shepherd.os.platform()) { case 'darwin': - fixPath(); - agamaDir = `${process.env.HOME}/Library/Application Support/Agama`; + shepherd.fixPath(); + shepherd.agamaDir = `${process.env.HOME}/Library/Application Support/Agama`; break; case 'linux': - agamaDir = `${process.env.HOME}/.agama`; + shepherd.agamaDir = `${process.env.HOME}/.agama`; break; case 'win32': - agamaDir = `${process.env.APPDATA}/Agama`; - agamaDir = path.normalize(agamaDir); + shepherd.agamaDir = `${process.env.APPDATA}/Agama`; + shepherd.agamaDir = shepherd.path.normalize(shepherd.agamaDir); break; } -shepherd.log = function(msg, isSpvOut) { - if (shepherd.appConfig.dev || - shepherd.appConfig.debug) { - console.log(msg); - } - - if (!isSpvOut) { - appRuntimeLog.push({ - time: Date.now(), - msg: msg, - }); - } else { - appRuntimeSPVLog.push({ - time: Date.now(), - msg: msg, - }); - } -}; - -shepherd.get('/log/runtime', function(req, res, next) { - - const successObj = { - msg: 'success', - result: req.query.spv && req.query.spv === 'true' ? appRuntimeSPVLog : appRuntimeLog, - }; - - res.end(JSON.stringify(successObj)); -}); - -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`, (err) => { - if (err) { - shepherd.log('error writing log file'); - } - }); - } else { - fs.writeFile(`${logLocation}/agamalog.txt`, `${timeFormatted} ${data}\r\n`, (err) => { - if (err) { - shepherd.log('error writing log file'); - } - }); - } - } -} - -shepherd.loadLocalConfig = () => { - 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 = (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 = (appSettings) => { - let appConfFileName = `${agamaDir}/config.json`; - - _fs.access(agamaDir, fs.constants.R_OK, (err) => { - if (!err) { - - const FixFilePermissions = () => { - return new Promise((resolve, reject) => { - const result = 'config.json file permissions updated to Read/Write'; - - fsnode.chmodSync(appConfFileName, '0666'); - - setTimeout(() => { - shepherd.log(result); - shepherd.writeLog(result); - resolve(result); - }, 1000); - }); - } - - const FsWrite = () => { - return new Promise((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', (err) => { - if (err) - return shepherd.log(err); - }); - - fsnode.chmodSync(appConfFileName, '0666'); - setTimeout(() => { - 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 = require('./log.js')(shepherd); +shepherd = require('./config.js')(shepherd); 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 (shepherd.os.platform() === 'darwin') { + shepherd.fixPath(); + shepherd.agamaTestDir = `${process.env.HOME}/Library/Application Support/Agama/test`, + shepherd.komododBin = shepherd.path.join(__dirname, '../assets/bin/osx/komodod'), + shepherd.komodocliBin = shepherd.path.join(__dirname, '../assets/bin/osx/komodo-cli'), + shepherd.komodoDir = shepherd.appConfig.dataDir.length ? shepherd.appConfig.dataDir : `${process.env.HOME}/Library/Application Support/Komodo`, + shepherd.zcashdBin = '/Applications/ZCashSwingWalletUI.app/Contents/MacOS/zcashd', + shepherd.zcashcliBin = '/Applications/ZCashSwingWalletUI.app/Contents/MacOS/zcash-cli', + shepherd.zcashDir = `${process.env.HOME}/Library/Application Support/Zcash`, + shepherd.zcashParamsDir = `${process.env.HOME}/Library/Application Support/ZcashParams`, + shepherd.chipsBin = shepherd.path.join(__dirname, '../assets/bin/osx/chipsd'), + shepherd.chipscliBin = shepherd.path.join(__dirname, '../assets/bin/osx/chips-cli'), + shepherd.chipsDir = `${process.env.HOME}/Library/Application Support/Chips`, + shepherd.coindRootDir = shepherd.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 (shepherd.os.platform() === 'linux') { + shepherd.agamaTestDir = `${process.env.HOME}/.agama/test`, + shepherd.komododBin = shepherd.path.join(__dirname, '../assets/bin/linux64/komodod'), + shepherd.komodocliBin = shepherd.path.join(__dirname, '../assets/bin/linux64/komodo-cli'), + shepherd.komodoDir = shepherd.appConfig.dataDir.length ? shepherd.appConfig.dataDir : `${process.env.HOME}/.komodo`, + shepherd.zcashParamsDir = `${process.env.HOME}/.zcash-params`, + shepherd.chipsBin = shepherd.path.join(__dirname, '../assets/bin/linux64/chipsd'), + shepherd.chipscliBin = shepherd.path.join(__dirname, '../assets/bin/linux64/chips-cli'), + shepherd.chipsDir = `${process.env.HOME}/.chips`, + shepherd.coindRootDir = shepherd.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); +if (shepherd.os.platform() === 'win32') { + shepherd.agamaTestDir = `${process.env.APPDATA}/Agama/test`; + shepherd.agamaTestDir = shepherd.path.normalize(shepherd.agamaTestDir); + shepherd.komododBin = shepherd.path.join(__dirname, '../assets/bin/win64/komodod.exe'), + shepherd.komododBin = shepherd.path.normalize(shepherd.komododBin), + shepherd.komodocliBin = shepherd.path.join(__dirname, '../assets/bin/win64/komodo-cli.exe'), + shepherd.komodocliBin = shepherd.path.normalize(shepherd.komodocliBin), + shepherd.komodoDir = shepherd.appConfig.dataDir.length ? shepherd.appConfig.dataDir : `${process.env.APPDATA}/Komodo`, + shepherd.komodoDir = shepherd.path.normalize(shepherd.komodoDir); + shepherd.chipsBin = shepherd.path.join(__dirname, '../assets/bin/win64/chipsd.exe'), + shepherd.chipsBin = shepherd.path.normalize(shepherd.chipsBin), + shepherd.chipscliBin = shepherd.path.join(__dirname, '../assets/bin/win64/chips-cli.exe'), + shepherd.chipscliBin = shepherd.path.normalize(shepherd.chipscliBin), + shepherd.chipsDir = `${process.env.APPDATA}/Chips`, + shepherd.chipsDir = shepherd.path.normalize(shepherd.chipsDir); + shepherd.zcashParamsDir = `${process.env.APPDATA}/ZcashParams`; + shepherd.zcashParamsDir = shepherd.path.normalize(shepherd.zcashParamsDir); + shepherd.coindRootDir = shepherd.path.join(__dirname, '../assets/bin/osx/dex/coind'); + shepherd.coindRootDir = shepherd.path.normalize(shepherd.coindRootDir); } -shepherd.appConfigSchema = _appConfig.schema; +shepherd.appConfigSchema = shepherd._appConfig.schema; shepherd.defaultAppConfig = Object.assign({}, shepherd.appConfig); shepherd.kmdMainPassiveMode = false; -shepherd.coindInstanceRegistry = coindInstanceRegistry; - shepherd.getNetworkData = (network) => { const coin = shepherd.findNetworkObj(network) || shepherd.findNetworkObj(network.toUpperCase()) || shepherd.findNetworkObj(network.toLowerCase()); const coinUC = coin ? coin.toUpperCase() : null; @@ -492,14 +182,14 @@ shepherd.getNetworkData = (network) => { coinUC === 'MESH' || coinUC === 'WLC' || coinUC === 'MNZ') { - return electrumJSNetworks.komodo; + return shepherd.electrumJSNetworks.komodo; } else { - return electrumJSNetworks[network]; + return shepherd.electrumJSNetworks[network]; } } shepherd.seedToWif = (seed, network, iguana) => { - const bytes = sha256(seed, { asBytes: true }); + const bytes = shepherd.sha256(seed, { asBytes: true }); if (iguana) { bytes[0] &= 248; @@ -515,7 +205,7 @@ shepherd.seedToWif = (seed, network, iguana) => { const hex = toHexString(bytes); - const key = new CoinKey(new Buffer(hex, 'hex'), { + const key = new shepherd.CoinKey(new Buffer(hex, 'hex'), { private: shepherd.getNetworkData(network).wif, public: shepherd.getNetworkData(network).pubKeyHash, }); @@ -545,23 +235,23 @@ shepherd.get('/electrum/seedtowif', (req, res, next) => { }); shepherd.findNetworkObj = (coin) => { - for (let key in electrumServers) { - if (electrumServers[key].abbr === coin) { + for (let key in shepherd.electrumServers) { + if (shepherd.electrumServers[key].abbr === coin) { return key; } } } shepherd.findCoinName = (network) => { - for (let key in electrumServers) { + for (let key in shepherd.electrumServers) { if (key === network) { - return electrumServers[key].abbr; + return shepherd.electrumServers[key].abbr; } } } shepherd.get('/electrum/servers/test', (req, res, next) => { - const ecl = new electrumJSCore(req.query.port, req.query.address, 'tcp'); // tcp or tls + const ecl = new shepherd.electrumJSCore(req.query.port, req.query.address, 'tcp'); // tcp or tls ecl.connect(); ecl.serverVersion() @@ -594,52 +284,52 @@ shepherd.get('/electrum/keys', (req, res, next) => { let _matchingKeyPairs = 0; let _electrumKeys = {}; - for (let key in electrumServers) { - const _abbr = electrumServers[key].abbr; + for (let key in shepherd.electrumServers) { + const _abbr = shepherd.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) { + if (shepherd.electrumKeys[_abbr].pub === pub && + shepherd.electrumKeys[_abbr].priv === priv) { _matchingKeyPairs++; } } if (req.query.active) { - _electrumKeys = JSON.parse(JSON.stringify(electrumKeys)); + _electrumKeys = JSON.parse(JSON.stringify(shepherd.electrumKeys)); for (let key in _electrumKeys) { - if (!electrumCoins[key]) { + if (!shepherd.electrumCoins[key]) { delete _electrumKeys[key]; } } } else { - _electrumKeys = electrumKeys; + _electrumKeys = shepherd.electrumKeys; } shepherd.log(JSON.stringify(_electrumKeys, null, '\t'), true); const successObj = { msg: 'success', - result: _matchingKeyPairs === Object.keys(electrumKeys).length ? _electrumKeys : false, + result: _matchingKeyPairs === Object.keys(shepherd.electrumKeys).length ? _electrumKeys : false, }; res.end(JSON.stringify(successObj)); }); shepherd.get('/electrum/login', (req, res, next) => { - for (let key in electrumServers) { - const _abbr = electrumServers[key].abbr; + for (let key in shepherd.electrumServers) { + const _abbr = shepherd.electrumServers[key].abbr; const { priv, pub } = shepherd.seedToWif(req.query.seed, shepherd.findNetworkObj(_abbr), req.query.iguana); - electrumKeys[_abbr] = { + shepherd.electrumKeys[_abbr] = { priv, pub, }; } - electrumCoins.auth = true; + shepherd.electrumCoins.auth = true; - shepherd.log(JSON.stringify(electrumKeys, null, '\t'), true); + shepherd.log(JSON.stringify(shepherd.electrumKeys, null, '\t'), true); const successObj = { msg: 'success', @@ -650,8 +340,8 @@ shepherd.get('/electrum/login', (req, res, next) => { }); shepherd.get('/electrum/dev/logout', (req, res, next) => { - electrumCoins.auth = false; - electrumKeys = {}; + shepherd.electrumCoins.auth = false; + shepherd.electrumKeys = {}; const successObj = { msg: 'success', @@ -682,14 +372,14 @@ shepherd.get('/electrum/dev/logout', (req, res, next) => { const successObj = { msg: 'success', result: { - servers: electrumServers, + servers: shepherd.electrumServers, }, }; res.end(JSON.stringify(successObj)); - console.log(bitcoinJS.networks.komodo); - const hdMaster = bitcoinJS.HDNode.fromSeedBuffer(seed, electrumJSNetworks.komodo); // seed from above + console.log(shepherd.bitcoinJS.networks.komodo); + const hdMaster = shepherd.bitcoinJS.HDNode.fromSeedBuffer(seed, shepherd.electrumJSNetworks.komodo); // seed from above const key1 = hdMaster.derivePath('m/0'); const key2 = hdMaster.derivePath('m/1'); @@ -699,7 +389,7 @@ shepherd.get('/electrum/dev/logout', (req, res, next) => { console.log(key1.keyPair.getAddress()); console.log(key2.keyPair.toWIF()); - const hdnode = bitcoinJS.HDNode.fromSeedBuffer(seed, electrumJSNetworks.komodo).deriveHardened(0).derive(0).derive(1); + const hdnode = shepherd.bitcoinJS.HDNode.fromSeedBuffer(seed, shepherd.electrumJSNetworks.komodo).deriveHardened(0).derive(0).derive(1); console.log(`address: ${hdnode.getAddress()}`); console.log(`priv (WIF): ${hdnode.keyPair.toWIF()}`); });*/ @@ -710,7 +400,7 @@ shepherd.getMerkleRoot = (txid, proof, pos) => { let hash = txid; let serialized; const _sha256 = (data) => { - return crypto.createHash('sha256').update(data).digest(); + return shepherd.crypto.createHash('sha256').update(data).digest(); } shepherd.log(`getMerkleRoot txid ${txid}`, true); @@ -749,9 +439,9 @@ shepherd.verifyMerkle = (txid, height, serverList, mainServer) => { const _randomServer = randomServer.split(':'); const _mainServer = mainServer.split(':'); - let ecl = new electrumJSCore(_mainServer[1], _mainServer[0], 'tcp'); // tcp or tls + let ecl = new shepherd.electrumJSCore(_mainServer[1], _mainServer[0], 'tcp'); // tcp or tls - return new Promise((resolve, reject) => { + return new shepherd.Promise((resolve, reject) => { shepherd.log(`main server: ${mainServer}`, true); shepherd.log(`verification server: ${randomServer}`, true); @@ -768,7 +458,7 @@ shepherd.verifyMerkle = (txid, height, serverList, mainServer) => { const _res = shepherd.getMerkleRoot(txid, merkleData.merkle, merkleData.pos); shepherd.log(_res, true); - ecl = new electrumJSCore(_randomServer[1], _randomServer[0], 'tcp'); + ecl = new shepherd.electrumJSCore(_randomServer[1], _randomServer[0], 'tcp'); ecl.connect(); ecl.blockchainBlockGetHeader(height) @@ -802,18 +492,18 @@ shepherd.verifyMerkle = (txid, height, serverList, mainServer) => { } shepherd.verifyMerkleByCoin = (coin, txid, height) => { - const _serverList = electrumCoins[coin].serverList; + const _serverList = shepherd.electrumCoins[coin].serverList; shepherd.log(`verifyMerkleByCoin`, true); - shepherd.log(electrumCoins[coin].server, true); - shepherd.log(electrumCoins[coin].serverList, true); + shepherd.log(shepherd.electrumCoins[coin].server, true); + shepherd.log(shepherd.electrumCoins[coin].serverList, true); - return new Promise((resolve, reject) => { + return new shepherd.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) { + if (_serverList[i] !== shepherd.electrumCoins[coin].server.ip + ':' + shepherd.electrumCoins[coin].server.port) { _filteredServerList.push(_serverList[i]); } } @@ -822,7 +512,7 @@ shepherd.verifyMerkleByCoin = (coin, txid, height) => { txid, height, _filteredServerList, - electrumCoins[coin].server.ip + ':' + electrumCoins[coin].server.port + shepherd.electrumCoins[coin].server.ip + ':' + shepherd.electrumCoins[coin].server.port ).then((proof) => { resolve(proof); }); @@ -850,8 +540,8 @@ shepherd.get('/electrum/servers', (req, res, next) => { if (req.query.abbr) { let _electrumServers = {}; - for (let key in electrumServers) { - _electrumServers[electrumServers[key].abbr] = electrumServers[key]; + for (let key in shepherd.electrumServers) { + _electrumServers[shepherd.electrumServers[key].abbr] = shepherd.electrumServers[key]; } const successObj = { @@ -866,7 +556,7 @@ shepherd.get('/electrum/servers', (req, res, next) => { const successObj = { msg: 'success', result: { - servers: electrumServers, + servers: shepherd.electrumServers, }, }; @@ -875,20 +565,20 @@ shepherd.get('/electrum/servers', (req, res, next) => { }); shepherd.get('/electrum/coins/server/set', (req, res, next) => { - electrumCoins[req.query.coin].server = { + shepherd.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; + for (let key in shepherd.electrumServers) { + if (shepherd.electrumServers[key].abbr === req.query.coin) { // a bit risky + shepherd.electrumServers[key].address = req.query.address; + shepherd.electrumServers[key].port = req.query.port; break; } } - shepherd.log(JSON.stringify(electrumCoins[req.query.coin], null, '\t'), true); + shepherd.log(JSON.stringify(shepherd.electrumCoins[req.query.coin], null, '\t'), true); const successObj = { msg: 'success', @@ -899,17 +589,17 @@ shepherd.get('/electrum/coins/server/set', (req, res, next) => { }); shepherd.addElectrumCoin = (coin) => { - for (let key in electrumServers) { - if (electrumServers[key].abbr === coin) { - electrumCoins[coin] = { + for (let key in shepherd.electrumServers) { + if (shepherd.electrumServers[key].abbr === coin) { + shepherd.electrumCoins[coin] = { name: key, abbr: coin, server: { - ip: electrumServers[key].address, - port: electrumServers[key].port, + ip: shepherd.electrumServers[key].address, + port: shepherd.electrumServers[key].port, }, - serverList: electrumServers[key].serverList ? electrumServers[key].serverList : 'none', - txfee: 'calculated' /*electrumServers[key].txfee*/, + serverList: shepherd.electrumServers[key].serverList ? shepherd.electrumServers[key].serverList : 'none', + txfee: 'calculated' /*shepherd.electrumServers[key].txfee*/, }; return true; @@ -918,7 +608,7 @@ shepherd.addElectrumCoin = (coin) => { } shepherd.get('/electrum/coins/remove', (req, res, next) => { - delete electrumCoins[req.query.coin]; + delete shepherd.electrumCoins[req.query.coin]; const successObj = { msg: 'success', @@ -940,11 +630,11 @@ shepherd.get('/electrum/coins/add', (req, res, next) => { }); shepherd.get('/electrum/coins', (req, res, next) => { - let _electrumCoins = JSON.parse(JSON.stringify(electrumCoins)); // deep cloning + let _electrumCoins = JSON.parse(JSON.stringify(shepherd.electrumCoins)); // deep cloning for (let key in _electrumCoins) { - if (electrumKeys[key]) { - _electrumCoins[key].pub = electrumKeys[key].pub; + if (shepherd.electrumKeys[key]) { + _electrumCoins[key].pub = shepherd.electrumKeys[key].pub; } } @@ -992,7 +682,7 @@ shepherd.kmdCalcInterest = (locktime, value) => { // value in sats shepherd.get('/electrum/getbalance', (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 + const ecl = new shepherd.electrumJSCore(shepherd.electrumServers[network].port, shepherd.electrumServers[network].address, shepherd.electrumServers[network].proto); // tcp or tls shepherd.log('electrum getbalance =>', true); @@ -1026,8 +716,8 @@ shepherd.get('/electrum/getbalance', (req, res, next) => { _utxo.length) { let interestTotal = 0; - Promise.all(_utxo.map((_utxoItem, index) => { - return new Promise((resolve, reject) => { + shepherd.Promise.all(_utxo.map((_utxoItem, index) => { + return new shepherd.Promise((resolve, reject) => { ecl.blockchainTransactionGet(_utxoItem['tx_hash']) .then((_rawtxJSON) => { shepherd.log('electrum gettransaction ==>', true); @@ -1036,7 +726,7 @@ shepherd.get('/electrum/getbalance', (req, res, next) => { // decode tx const _network = shepherd.getNetworkData(network); - const decodedTx = electrumJSTxDecoder(_rawtxJSON, _network); + const decodedTx = shepherd.electrumJSTxDecoder(_rawtxJSON, _network); if (decodedTx && decodedTx.format && @@ -1149,7 +839,7 @@ shepherd.sortTransactions = (transactions) => { shepherd.get('/electrum/listtransactions', (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 + const ecl = new shepherd.electrumJSCore(shepherd.electrumServers[network].port, shepherd.electrumServers[network].address, shepherd.electrumServers[network].proto); // tcp or tls shepherd.log('electrum listtransactions ==>', true); @@ -1189,8 +879,8 @@ shepherd.get('/electrum/listtransactions', (req, res, next) => { shepherd.log(json.length, true); - Promise.all(json.map((transaction, index) => { - return new Promise((resolve, reject) => { + shepherd.Promise.all(json.map((transaction, index) => { + return new shepherd.Promise((resolve, reject) => { ecl.blockchainBlockGetHeader(transaction.height) .then((blockInfo) => { if (blockInfo && @@ -1203,7 +893,7 @@ shepherd.get('/electrum/listtransactions', (req, res, next) => { // decode tx const _network = shepherd.getNetworkData(network); - const decodedTx = electrumJSTxDecoder(_rawtxJSON, _network); + const decodedTx = shepherd.electrumJSTxDecoder(_rawtxJSON, _network); let txInputs = []; @@ -1212,12 +902,12 @@ shepherd.get('/electrum/listtransactions', (req, res, next) => { if (decodedTx && decodedTx.inputs) { - Promise.all(decodedTx.inputs.map((_decodedInput, index) => { - return new Promise((_resolve, _reject) => { + shepherd.Promise.all(decodedTx.inputs.map((_decodedInput, index) => { + return new shepherd.Promise((_resolve, _reject) => { if (_decodedInput.txid !== '0000000000000000000000000000000000000000000000000000000000000000') { ecl.blockchainTransactionGet(_decodedInput.txid) .then((rawInput) => { - const decodedVinVout = electrumJSTxDecoder(rawInput, _network); + const decodedVinVout = shepherd.electrumJSTxDecoder(rawInput, _network); shepherd.log('electrum raw input tx ==>', true); @@ -1342,7 +1032,7 @@ shepherd.get('/electrum/listtransactions', (req, res, next) => { shepherd.get('/electrum/gettransaction', (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 + const ecl = new shepherd.electrumJSCore(shepherd.electrumServers[network].port, shepherd.electrumServers[network].address, shepherd.electrumServers[network].proto); // tcp or tls shepherd.log('electrum gettransaction =>', true); @@ -1491,8 +1181,8 @@ shepherd.get('/electrum/getblockinfo', (req, res, next) => { }); shepherd.electrumGetBlockInfo = (height, network) => { - return new Promise((resolve, reject) => { - const ecl = new electrumJSCore(electrumServers[network].port, electrumServers[network].address, electrumServers[network].proto); // tcp or tls + return new shepherd.Promise((resolve, reject) => { + const ecl = new shepherd.electrumJSCore(shepherd.electrumServers[network].port, shepherd.electrumServers[network].address, shepherd.electrumServers[network].proto); // tcp or tls ecl.connect(); ecl.blockchainBlockGetHeader(height) @@ -1519,8 +1209,8 @@ shepherd.get('/electrum/getcurrentblock', (req, res, next) => { }); shepherd.electrumGetCurrentBlock = (network) => { - return new Promise((resolve, reject) => { - const ecl = new electrumJSCore(electrumServers[network].port, electrumServers[network].address, electrumServers[network].proto); // tcp or tls + return new shepherd.Promise((resolve, reject) => { + const ecl = new shepherd.electrumJSCore(shepherd.electrumServers[network].port, shepherd.electrumServers[network].address, shepherd.electrumServers[network].proto); // tcp or tls ecl.connect(); ecl.blockchainNumblocksSubscribe() @@ -1538,7 +1228,7 @@ shepherd.get('/electrum/decoderawtx', (req, res, next) => { const _network = shepherd.getNetworkData(req.query.network); const _rawtx = req.query.rawtx; // const _rawtx = '0100000001dd6d064f5665f8454293ecaa9dbb55accf4f7e443d35f3b5ab7760f54b6c15fe000000006a473044022056355585a4a501ec9afc96aa5df124cf29ad3ac6454b47cd07cd7d89ec95ec2b022074c4604ee349d30e5336f210598e4dc576bf16ebeb67eeac3f4e82f56e930fee012103b90ba01af308757054e0484bb578765d5df59c4a57adbb94e2419df5e7232a63feffffff0289fc923b000000001976a91424af38fcb13bbc171b0b42bb017244a53b6bb2fa88ac20a10700000000001976a9142f4c0f91fc06ac228c120aee41741d0d3909683288ac49258b58'; - const decodedTx = electrumJSTxDecoder(_rawtx, _network); + const decodedTx = shepherd.electrumJSTxDecoder(_rawtx, _network); shepherd.log('electrum decoderawtx input tx ==>', true); @@ -1558,7 +1248,7 @@ shepherd.get('/electrum/decoderawtx', (req, res, next) => { 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 + const ecl = new shepherd.electrumJSCore(shepherd.electrumServers[req.query.network].port, shepherd.electrumServers[req.query.network].address, shepherd.electrumServers[req.query.network].proto); // tcp or tls ecl.connect(); ecl.blockchainTransactionGet(decodedTx.inputs[0].txid) @@ -1566,7 +1256,7 @@ shepherd.get('/electrum/decoderawtx', (req, res, next) => { ecl.close(); shepherd.log(json, true); - const decodedVin = electrumJSTxDecoder(json, _network); + const decodedVin = shepherd.electrumJSTxDecoder(json, _network); const successObj = { msg: 'success', @@ -1626,7 +1316,7 @@ shepherd.findUtxoSet = function(utxoList, target) { // remove 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 _utxoSet = shepherd.findUtxoSet(null, Number(req.query.target) + Number(shepherd.electrumServers[req.query.network].txfee)); // target + txfee const successObj = { msg: 'success', @@ -1641,13 +1331,13 @@ shepherd.get('/electrum/subset', function(req, res, next) { // single sig shepherd.buildSignedTx = (sendTo, changeAddress, wif, network, utxo, changeValue, spendValue) => { - let key = bitcoinJS.ECPair.fromWIF(wif, shepherd.getNetworkData(network)); - let tx = new bitcoinJS.TransactionBuilder(shepherd.getNetworkData(network)); + let key = shepherd.bitcoinJS.ECPair.fromWIF(wif, shepherd.getNetworkData(network)); + let tx = new shepherd.bitcoinJS.TransactionBuilder(shepherd.getNetworkData(network)); shepherd.log('buildSignedTx'); // console.log(`buildSignedTx priv key ${wif}`); shepherd.log(`buildSignedTx pub key ${key.getAddress().toString()}`, true); - // console.log('buildSignedTx std tx fee ' + electrumServers[network].txfee); + // console.log('buildSignedTx std tx fee ' + shepherd.electrumServers[network].txfee); for (let i = 0; i < utxo.length; i++) { tx.addInput(utxo[i].txid, utxo[i].vout); @@ -1702,16 +1392,16 @@ shepherd.maxSpendBalance = (utxoList, fee) => { shepherd.get('/electrum/createrawtx', (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 ecl = new shepherd.electrumJSCore(shepherd.electrumServers[network].port, shepherd.electrumServers[network].address, shepherd.electrumServers[network].proto); // tcp or tls const outputAddress = req.query.address; const changeAddress = req.query.change; const value = Number(req.query.value); const push = req.query.push; - const fee = electrumServers[network].txfee; + const fee = shepherd.electrumServers[network].txfee; let wif = req.query.wif; if (req.query.gui) { - wif = electrumKeys[req.query.coin].priv; + wif = shepherd.electrumKeys[req.query.coin].priv; } shepherd.log('electrum createrawtx =>', true); @@ -1767,7 +1457,7 @@ shepherd.get('/electrum/createrawtx', (req, res, next) => { // 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); + let { inputs, outputs, fee } = shepherd.coinSelect(utxoListFormatted, targets, feeRate); shepherd.log('coinselect res =>', true); shepherd.log('coinselect inputs =>', true); @@ -1779,12 +1469,12 @@ shepherd.get('/electrum/createrawtx', (req, res, next) => { if (!inputs && !outputs) { - targets[0].value = targets[0].value - electrumServers[network].txfee; + targets[0].value = targets[0].value - shepherd.electrumServers[network].txfee; shepherd.log('second run', true); shepherd.log('coinselect adjusted targets =>', true); shepherd.log(targets, true); - const secondRun = coinSelect(utxoListFormatted, targets, feeRate); + const secondRun = shepherd.coinSelect(utxoListFormatted, targets, feeRate); inputs = secondRun.inputs; outputs = secondRun.outputs; fee = secondRun.fee; @@ -1900,7 +1590,7 @@ shepherd.get('/electrum/createrawtx', (req, res, next) => { res.end(JSON.stringify(successObj)); } else { - const ecl = new electrumJSCore(electrumServers[network].port, electrumServers[network].address, electrumServers[network].proto); // tcp or tls + const ecl = new shepherd.electrumJSCore(shepherd.electrumServers[network].port, shepherd.electrumServers[network].address, shepherd.electrumServers[network].proto); // tcp or tls ecl.connect(); ecl.blockchainTransactionBroadcast(_rawtx) @@ -2029,7 +1719,7 @@ shepherd.get('/electrum/createrawtx', (req, res, next) => { shepherd.get('/electrum/pushtx', (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 + const ecl = new shepherd.electrumJSCore(shepherd.electrumServers[req.query.network].port, shepherd.electrumServers[req.query.network].address, shepherd.electrumServers[req.query.network].proto); // tcp or tls ecl.connect(); ecl.blockchainTransactionBroadcast(rawtx) @@ -2051,7 +1741,7 @@ shepherd.listunspent = (ecl, address, network, full, verify) => { let _atLeastOneDecodeTxFailed = false; if (full) { - return new Promise((resolve, reject) => { + return new shepherd.Promise((resolve, reject) => { ecl.connect(); ecl.blockchainAddressListunspent(address) .then((_utxoJSON) => { @@ -2074,8 +1764,8 @@ shepherd.listunspent = (ecl, address, network, full, verify) => { if (!_utxo.length) { // no confirmed utxo resolve('no valid utxo'); } else { - Promise.all(_utxo.map((_utxoItem, index) => { - return new Promise((resolve, reject) => { + shepherd.Promise.all(_utxo.map((_utxoItem, index) => { + return new shepherd.Promise((resolve, reject) => { ecl.blockchainTransactionGet(_utxoItem['tx_hash']) .then((_rawtxJSON) => { shepherd.log('electrum gettransaction ==>', true); @@ -2084,7 +1774,7 @@ shepherd.listunspent = (ecl, address, network, full, verify) => { // decode tx const _network = shepherd.getNetworkData(network); - const decodedTx = electrumJSTxDecoder(_rawtxJSON, _network); + const decodedTx = shepherd.electrumJSTxDecoder(_rawtxJSON, _network); shepherd.log('decoded tx =>', true); shepherd.log(decodedTx, true); @@ -2183,7 +1873,7 @@ shepherd.listunspent = (ecl, address, network, full, verify) => { }); }); } else { - return new Promise((resolve, reject) => { + return new shepherd.Promise((resolve, reject) => { ecl.connect(); ecl.blockchainAddressListunspent(address) .then((json) => { @@ -2202,7 +1892,7 @@ shepherd.listunspent = (ecl, address, network, full, verify) => { shepherd.get('/electrum/listunspent', (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 + const ecl = new shepherd.electrumJSCore(shepherd.electrumServers[network].port, shepherd.electrumServers[network].address, shepherd.electrumServers[network].proto); // tcp or tls if (req.query.full && req.query.full === 'true') { @@ -2239,7 +1929,7 @@ shepherd.get('/electrum/listunspent', (req, res, next) => { }); shepherd.get('/electrum/estimatefee', (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 + const ecl = new shepherd.electrumJSCore(shepherd.electrumServers[req.query.network].port, shepherd.electrumServers[req.query.network].address, shepherd.electrumServers[req.query.network].proto); // tcp or tls ecl.connect(); ecl.blockchainEstimatefee(req.query.blocks) @@ -2279,22 +1969,22 @@ shepherd.scanNativeCoindBins = () => { let nativeCoindList = {}; // check if coind bins are present in agama - for (let key in nativeCoind) { + for (let key in shepherd.nativeCoind) { nativeCoindList[key] = { - name: nativeCoind[key].name, - port: nativeCoind[key].port, - bin: nativeCoind[key].bin, + name: shepherd.nativeCoind[key].name, + port: shepherd.nativeCoind[key].port, + bin: shepherd.nativeCoind[key].bin, bins: { daemon: false, cli: false, }, }; - if (fs.existsSync(`${coindRootDir}/${key}/${nativeCoind[key].bin}d${os.platform() === 'win32' ? '.exe' : ''}`)) { + if (shepherd.fs.existsSync(`${shepherd.coindRootDir}/${key}/${shepherd.nativeCoind[key].bin}d${shepherd.os.platform() === 'win32' ? '.exe' : ''}`)) { nativeCoindList[key].bins.daemon = true; } - if (fs.existsSync(`${coindRootDir}/${key}/${nativeCoind[key].bin}-cli${os.platform() === 'win32' ? '.exe' : ''}`)) { + if (shepherd.fs.existsSync(`${shepherd.coindRootDir}/${key}/${shepherd.nativeCoind[key].bin}-cli${shepherd.os.platform() === 'win32' ? '.exe' : ''}`)) { nativeCoindList[key].bins.cli = true; } } @@ -2303,7 +1993,7 @@ shepherd.scanNativeCoindBins = () => { } shepherd.getAppRuntimeLog = () => { - return new Promise((resolve, reject) => { + return new shepherd.Promise((resolve, reject) => { resolve(appRuntimeLog); }); }; @@ -2344,7 +2034,7 @@ shepherd.startKMDNative = (selection, isManual) => { }) }; - request(options, (error, response, body) => { + shepherd.request(options, (error, response, body) => { if (response && response.statusCode && response.statusCode === 200) { @@ -2394,7 +2084,7 @@ shepherd.startKMDNative = (selection, isManual) => { }) }; - request(options, (error, response, body) => { + shepherd.request(options, (error, response, body) => { if (response && response.statusCode && response.statusCode === 200) { @@ -2458,8 +2148,8 @@ shepherd.post('/native/dashboard/update', (req, res, next) => { type.pop(); } - Promise.all(type.map((_type, index) => { - return new Promise((resolve, reject) => { + shepherd.Promise.all(type.map((_type, index) => { + return new shepherd.Promise((resolve, reject) => { _bitcoinRPC( coin, _type === 'public' ? 'getaddressesbyaccount' : 'z_listaddresses', @@ -2545,8 +2235,8 @@ shepherd.post('/native/dashboard/update', (req, res, next) => { // get zaddr balance if (result[1] && result[1].length) { - Promise.all(result[1].map((_address, index) => { - return new Promise((resolve, reject) => { + shepherd.Promise.all(result[1].map((_address, index) => { + return new shepherd.Promise((resolve, reject) => { _bitcoinRPC(coin, 'z_getbalance', [_address]) .then((__json) => { __json = JSON.parse(__json); @@ -2628,7 +2318,7 @@ shepherd.post('/native/dashboard/update', (req, res, next) => { } const _bitcoinRPC = (coin, cmd, params) => { - return new Promise((resolve, reject) => { + return new shepherd.Promise((resolve, reject) => { let _payload; if (params) { @@ -2656,7 +2346,7 @@ shepherd.post('/native/dashboard/update', (req, res, next) => { timeout: 120000, }; - request(options, (error, response, body) => { + shepherd.request(options, (error, response, body) => { if (response && response.statusCode && response.statusCode === 200) { @@ -2668,7 +2358,7 @@ shepherd.post('/native/dashboard/update', (req, res, next) => { }); } - Promise.all(_promiseStack.map((_call, index) => { + shepherd.Promise.all(_promiseStack.map((_call, index) => { let _params; if (_call === 'listtransactions') { _params = [ @@ -2677,7 +2367,7 @@ shepherd.post('/native/dashboard/update', (req, res, next) => { 0 ]; } - return new Promise((resolve, reject) => { + return new shepherd.Promise((resolve, reject) => { _bitcoinRPC( _coin, _call, @@ -2700,54 +2390,54 @@ shepherd.post('/native/dashboard/update', (req, res, next) => { }); shepherd.testClearAll = () => { - return new Promise((resolve, reject) => { - fs.removeSync(`${iguanaTestDir}`); + return new shepherd.Promise((resolve, reject) => { + shepherd.fs.removeSync(`${iguanaTestDir}`); resolve('done'); }); } shepherd.testBins = (daemonName) => { - return new Promise((resolve, reject) => { + return new shepherd.Promise((resolve, reject) => { const _bins = { - komodod: komododBin, - komodoCli: komodocliBin, + komodod: shepherd.komododBin, + komodoCli: shepherd.komodocliBin, }; const _arg = null; let _pid; shepherd.log('testBins exec ' + _bins[daemonName]); - if (!fs.existsSync(agamaTestDir)) { - fs.mkdirSync(agamaTestDir); + if (!shepherd.fs.existsSync(shepherd.agamaTestDir)) { + shepherd.fs.mkdirSync(shepherd.agamaTestDir); } try { - _fs.access(`${agamaTestDir}/${daemonName}Test.log`, fs.constants.R_OK, (err) => { + shepherd._fs.access(`${shepherd.agamaTestDir}/${daemonName}Test.log`, shepherd.fs.constants.R_OK, (err) => { if (!err) { try { - _fs.unlinkSync(`${agamaTestDir}/${daemonName}Test.log`); + shepherd._fs.unlinkSync(`${shepherd.agamaTestDir}/${daemonName}Test.log`); } catch (e) {} } else { - shepherd.log(`path ${agamaTestDir}/${daemonName}Test.log doesnt exist`); + shepherd.log(`path ${shepherd.agamaTestDir}/${daemonName}Test.log doesnt exist`); } }); } catch (e) {} if (daemonName === 'komodod') { try { - _fs.access(`${iguanaTestDir}/debug.log`, fs.constants.R_OK, (err) => { + shepherd._fs.access(`${iguanaTestDir}/debug.log`, shepherd.fs.constants.R_OK, (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`); + shepherd._fs.unlinkSync(`${iguanaTestDir}/db.log`); + shepherd._fs.unlinkSync(`${iguanaTestDir}/debug.log`); + shepherd._fs.unlinkSync(`${iguanaTestDir}/komodo.conf`); + shepherd._fs.unlinkSync(`${iguanaTestDir}/komodod.pid`); + shepherd._fs.unlinkSync(`${iguanaTestDir}/komodostate`); + shepherd._fs.unlinkSync(`${iguanaTestDir}/realtime`); + shepherd._fs.unlinkSync(`${iguanaTestDir}/wallet.dat`); + shepherd._fs.unlinkSync(`${iguanaTestDir}/.lock`); + shepherd.fs.removeSync(`${iguanaTestDir}/blocks`); + shepherd.fs.removeSync(`${iguanaTestDir}/chainstate`); + shepherd.fs.removeSync(`${iguanaTestDir}/database`); execKomodod(); } else { shepherd.log(`test: nothing to remove in ${iguanaTestDir}`); @@ -2790,13 +2480,13 @@ shepherd.testBins = (daemonName) => { 'addnode=185.106.121.32\n' + 'addnode=27.100.36.201\n'; - fs.writeFile(`${iguanaTestDir}/komodo.conf`, _komodoConf, (err) => { + shepherd.fs.writeFile(`${iguanaTestDir}/komodo.conf`, _komodoConf, (err) => { if (err) { shepherd.log(`test: error writing komodo conf in ${iguanaTestDir}`); } }); - portscanner.checkPortStatus('7771', '127.0.0.1', (error, status) => { + shepherd.portscanner.checkPortStatus('7771', '127.0.0.1', (error, status) => { // Status is 'open' if currently in use or 'closed' if available if (status === 'closed') { _komododTest.port = 'passed'; @@ -2812,7 +2502,7 @@ shepherd.testBins = (daemonName) => { } pm2.start({ - script: komododBin, // path to binary + script: shepherd.komododBin, // path to binary name: 'komodod', exec_mode : 'fork', args: [ @@ -2857,7 +2547,7 @@ shepherd.testBins = (daemonName) => { }), }; - request(options, (error, response, body) => { + shepherd.request(options, (error, response, body) => { if (response && response.statusCode && response.statusCode === 200) { @@ -2888,7 +2578,7 @@ shepherd.testBins = (daemonName) => { //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) - /*execFile(`${komododBin}`, _arg, { + /*shepherd.execFile(`${shepherd.komododBin}`, _arg, { maxBuffer: 1024 * 10000 // 10 mb }, function(error, stdout, stderr) { shepherd.writeLog(`stdout: ${stdout}`); @@ -2913,12 +2603,12 @@ shepherd.testBins = (daemonName) => { // komodod datadir location test shepherd.testLocation = (path) => { - return new Promise((resolve, reject) => { - if (path.indexOf(' ') > -1) { + return new shepherd.Promise((resolve, reject) => { + if (shepherd.path.indexOf(' ') > -1) { shepherd.log(`error testing path ${path}`); resolve(-1); } else { - fs.lstat(path, (err, stats) => { + shepherd.fs.lstat(path, (err, stats) => { if (err) { shepherd.log(`error testing path ${path}`); resolve(-1); @@ -2937,20 +2627,20 @@ shepherd.testLocation = (path) => { // osx and linux shepherd.binFixRights = () => { - const osPlatform = os.platform(); + const osPlatform = shepherd.os.platform(); const _bins = [ - komododBin, - komodocliBin + shepherd.komododBin, + shepherd.komodocliBin ]; if (osPlatform === 'darwin' || osPlatform === 'linux') { for (let i = 0; i < _bins.length; i++) { - _fs.stat(_bins[i], (err, stat) => { + shepherd._fs.stat(_bins[i], (err, stat) => { if (!err) { if (parseInt(stat.mode.toString(8), 10) !== 100775) { shepherd.log(`${_bins[i]} fix permissions`); - fsnode.chmodSync(_bins[i], '0775'); + shepherd.fsnode.chmodSync(_bins[i], '0775'); } } else { shepherd.log(`error: ${_bins[i]} not found`); @@ -2963,7 +2653,7 @@ shepherd.binFixRights = () => { shepherd.killRogueProcess = (processName) => { // kill rogue process copies on start let processGrep; - const osPlatform = os.platform(); + const osPlatform = shepherd.os.platform(); switch (osPlatform) { case 'darwin': @@ -2977,14 +2667,14 @@ shepherd.killRogueProcess = (processName) => { break; } - exec(processGrep, (error, stdout, stderr) => { + shepherd.exec(processGrep, (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, (error, stdout, stderr) => { + shepherd.exec(pkillCmd, (error, stdout, stderr) => { shepherd.log(`${pkillCmd} is issued`); shepherd.writeLog(`${pkillCmd} is issued`); @@ -3004,10 +2694,10 @@ shepherd.killRogueProcess = (processName) => { shepherd.zcashParamsExist = () => { let _checkList = { - rootDir: _fs.existsSync(zcashParamsDir), - provingKey: _fs.existsSync(`${zcashParamsDir}/sprout-proving.key`), + rootDir: shepherd._fs.existsSync(shepherd.zcashParamsDir), + provingKey: shepherd._fs.existsSync(`${shepherd.zcashParamsDir}/sprout-proving.key`), provingKeySize: false, - verifyingKey: _fs.existsSync(`${zcashParamsDir}/sprout-verifying.key`), + verifyingKey: shepherd._fs.existsSync(`${shepherd.zcashParamsDir}/sprout-verifying.key`), verifyingKeySize: false, errors: false, }; @@ -3016,8 +2706,8 @@ shepherd.zcashParamsExist = () => { _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; + const _provingKeySize = _checkList.provingKey ? shepherd.fs.lstatSync(`${shepherd.zcashParamsDir}/sprout-proving.key`) : 0; + const _verifyingKeySize = _checkList.verifyingKey ? shepherd.fs.lstatSync(`${shepherd.zcashParamsDir}/sprout-verifying.key`) : 0; if (Number(_provingKeySize.size) === 910173851) { // bytes _checkList.provingKeySize = true; @@ -3044,41 +2734,41 @@ shepherd.zcashParamsExist = () => { shepherd.readVersionFile = () => { // read app version - const rootLocation = path.join(__dirname, '../'); - const localVersionFile = fs.readFileSync(`${rootLocation}version`, 'utf8'); + const rootLocation = shepherd.path.join(__dirname, '../'); + const localVersionFile = shepherd.fs.readFileSync(`${rootLocation}version`, 'utf8'); return localVersionFile; } shepherd.createAgamaDirs = () => { - if (!fs.existsSync(agamaDir)) { - fs.mkdirSync(agamaDir); + if (!shepherd.fs.existsSync(shepherd.agamaDir)) { + shepherd.fs.mkdirSync(shepherd.agamaDir); - if (fs.existsSync(agamaDir)) { - shepherd.log(`created agama folder at ${agamaDir}`); - shepherd.writeLog(`created agama folder at ${agamaDir}`); + if (shepherd.fs.existsSync(shepherd.agamaDir)) { + shepherd.log(`created agama folder at ${shepherd.agamaDir}`); + shepherd.writeLog(`created agama folder at ${shepherd.agamaDir}`); } } else { shepherd.log('agama folder already exists'); } - if (!fs.existsSync(`${agamaDir}/shepherd`)) { - fs.mkdirSync(`${agamaDir}/shepherd`); + if (!shepherd.fs.existsSync(`${shepherd.agamaDir}/shepherd`)) { + shepherd.fs.mkdirSync(`${shepherd.agamaDir}/shepherd`); - if (fs.existsSync(`${agamaDir}/shepherd`)) { - shepherd.log(`created shepherd folder at ${agamaDir}/shepherd`); - shepherd.writeLog(`create shepherd folder at ${agamaDir}/shepherd`); + if (shepherd.fs.existsSync(`${shepherd.agamaDir}/shepherd`)) { + shepherd.log(`created shepherd folder at ${shepherd.agamaDir}/shepherd`); + shepherd.writeLog(`create shepherd folder at ${shepherd.agamaDir}/shepherd`); } } else { shepherd.log('agama/shepherd folder already exists'); } - if (!fs.existsSync(`${agamaDir}/shepherd/pin`)) { - fs.mkdirSync(`${agamaDir}/shepherd/pin`); + if (!shepherd.fs.existsSync(`${shepherd.agamaDir}/shepherd/pin`)) { + shepherd.fs.mkdirSync(`${shepherd.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`); + if (shepherd.fs.existsSync(`${shepherd.agamaDir}/shepherd/pin`)) { + shepherd.log(`created pin folder at ${shepherd.agamaDir}/shepherd/pin`); + shepherd.writeLog(`create pin folder at ${shepherd.agamaDir}/shepherd/pin`); } } else { shepherd.log('shepherd/pin folder already exists'); @@ -3093,7 +2783,7 @@ shepherd.post('/encryptkey', (req, res, next) => { if (req.body.key && req.body.string && req.body.pubkey) { - const encryptedString = aes256.encrypt(req.body.key, req.body.string); + const encryptedString = shepherd.aes256.encrypt(req.body.key, req.body.string); // test pin security // - at least 1 char in upper case @@ -3104,7 +2794,7 @@ shepherd.post('/encryptkey', (req, res, next) => { 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, (err) => { + shepherd.fs.writeFile(`${shepherd.agamaDir}/shepherd/pin/${req.body.pubkey}.pin`, encryptedString, (err) => { if (err) { shepherd.log('error writing pin file'); } @@ -3142,8 +2832,8 @@ shepherd.post('/encryptkey', (req, res, next) => { shepherd.post('/decryptkey', (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', (err, data) => { + if (shepherd.fs.existsSync(`${shepherd.agamaDir}/shepherd/pin/${req.body.pubkey}.pin`)) { + shepherd.fs.readFile(`${shepherd.agamaDir}/shepherd/pin/${req.body.pubkey}.pin`, 'utf8', (err, data) => { if (err) { const errorObj = { msg: 'error', @@ -3152,7 +2842,7 @@ shepherd.post('/decryptkey', (req, res, next) => { res.end(JSON.stringify(errorObj)); } else { - const encryptedKey = aes256.decrypt(req.body.key, data); + const encryptedKey = shepherd.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); @@ -3192,8 +2882,8 @@ shepherd.post('/decryptkey', (req, res, next) => { }); shepherd.get('/getpinlist', (req, res, next) => { - if (fs.existsSync(`${agamaDir}/shepherd/pin`)) { - fs.readdir(`${agamaDir}/shepherd/pin`, (err, items) => { + if (shepherd.fs.existsSync(`${shepherd.agamaDir}/shepherd/pin`)) { + shepherd.fs.readdir(`${shepherd.agamaDir}/shepherd/pin`, (err, items) => { let _pins = []; for (let i = 0; i < items.length; i++) { @@ -3232,12 +2922,12 @@ shepherd.get('/getpinlist', (req, res, next) => { * Promise based download file method */ const downloadFile = (configuration) => { - return new Promise((resolve, reject) => { + return new shepherd.Promise((resolve, reject) => { // Save variable to know progress let receivedBytes = 0; let totalBytes = 0; - let req = request({ + let req = shepherd.request({ method: 'GET', uri: configuration.remoteFile, agentOptions: { @@ -3246,7 +2936,7 @@ const downloadFile = (configuration) => { }, }); - let out = fs.createWriteStream(configuration.localFile); + let out = shepherd.fs.createWriteStream(configuration.localFile); req.pipe(out); req.on('response', (data) => { @@ -3321,7 +3011,7 @@ let binsToUpdate = []; */ // TODO: promises shepherd.get('/update/bins/check', (req, res, next) => { - const rootLocation = path.join(__dirname, '../'); + const rootLocation = shepherd.path.join(__dirname, '../'); const successObj = { msg: 'success', result: 'bins', @@ -3329,7 +3019,7 @@ shepherd.get('/update/bins/check', (req, res, next) => { res.end(JSON.stringify(successObj)); - const _os = os.platform(); + const _os = shepherd.os.platform(); shepherd.log(`checking bins: ${_os}`); shepherd.io.emit('patch', { @@ -3341,8 +3031,8 @@ shepherd.get('/update/bins/check', (req, res, next) => { }); // 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], (err, remoteBinSize) => { - const localBinSize = fs.statSync(rootLocation + localBinLocation[_os] + latestBins[_os][i]).size; + shepherd.remoteFileSize(remoteBinLocation[_os] + latestBins[_os][i], (err, remoteBinSize) => { + const localBinSize = shepherd.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 + ')'); @@ -3375,8 +3065,8 @@ shepherd.get('/update/bins/check', (req, res, next) => { * params: */ shepherd.get('/update/bins', (req, res, next) => { - const rootLocation = path.join(__dirname, '../'); - const _os = os.platform(); + const rootLocation = shepherd.path.join(__dirname, '../'); + const _os = shepherd.os.platform(); const successObj = { msg: 'success', result: { @@ -3410,7 +3100,7 @@ shepherd.get('/update/bins', (req, res, next) => { }) .then(() => { // verify that remote file is matching to DL'ed file - const localBinSize = fs.statSync(`${rootLocation}${localBinLocation[_os]}patch/${binsToUpdate[i].name}`).size; + const localBinSize = shepherd.fs.statSync(`${rootLocation}${localBinLocation[_os]}patch/${binsToUpdate[i].name}`).size; shepherd.log('compare dl file size'); if (localBinSize === binsToUpdate[i].rSize) { @@ -3453,7 +3143,7 @@ shepherd.get('/update/patch', (req, res, next) => { }); shepherd.updateAgama = () => { - const rootLocation = path.join(__dirname, '../'); + const rootLocation = shepherd.path.join(__dirname, '../'); downloadFile({ remoteFile: 'https://github.com/pbca26/dl-test/raw/master/patch.zip', @@ -3476,20 +3166,20 @@ shepherd.updateAgama = () => { } }) .then(() => { - remoteFileSize('https://github.com/pbca26/dl-test/raw/master/patch.zip', (err, remotePatchSize) => { + shepherd.remoteFileSize('https://github.com/pbca26/dl-test/raw/master/patch.zip', (err, remotePatchSize) => { // verify that remote file is matching to DL'ed file - const localPatchSize = fs.statSync(`${rootLocation}patch.zip`).size; + const localPatchSize = shepherd.fs.statSync(`${rootLocation}patch.zip`).size; shepherd.log('compare dl file size'); if (localPatchSize === remotePatchSize) { - const zip = new AdmZip(`${rootLocation}patch.zip`); + const zip = new shepherd.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`); + if (!shepherd.fs.existsSync(`${rootLocation}/patch`)) { + shepherd.fs.mkdirSync(`${rootLocation}/patch`); } } @@ -3501,7 +3191,7 @@ shepherd.updateAgama = () => { status: 'done', }, }); - fs.unlinkSync(`${rootLocation}patch.zip`); + shepherd.fs.unlinkSync(`${rootLocation}patch.zip`); } else { shepherd.io.emit('patch', { msg: { @@ -3522,18 +3212,18 @@ shepherd.updateAgama = () => { * params: */ shepherd.get('/update/patch/check', (req, res, next) => { - const rootLocation = path.join(__dirname, '../'); + const rootLocation = shepherd.path.join(__dirname, '../'); const options = { url: 'https://github.com/pbca26/dl-test/raw/master/version', method: 'GET', }; - request(options, (error, response, body) => { + shepherd.request(options, (error, response, body) => { if (response && response.statusCode && response.statusCode === 200) { const remoteVersion = body.split('\n'); - const localVersionFile = fs.readFileSync(`${rootLocation}version`, 'utf8'); + const localVersionFile = shepherd.fs.readFileSync(`${rootLocation}version`, 'utf8'); let localVersion; if (localVersionFile.indexOf('\r\n') > -1) { @@ -3575,8 +3265,8 @@ shepherd.get('/update/patch/check', (req, res, next) => { * params: */ shepherd.get('/unpack', (req, res, next) => { - const dlLocation = path.join(__dirname, '../'); - const zip = new AdmZip(`${dlLocation}patch.zip`); + const dlLocation = shepherd.path.join(__dirname, '../'); + const zip = new shepherd.AdmZip(`${dlLocation}patch.zip`); zip.extractAllTo(/*target path*/ `${dlLocation}/patch/unpack`, /*overwrite*/true); const successObj = { @@ -3593,8 +3283,8 @@ shepherd.get('/unpack', (req, res, next) => { * params: */ shepherd.get('/zcparamsdl', (req, res, next) => { - // const dlLocation = zcashParamsDir + '/test'; - const dlLocation = zcashParamsDir; + // const dlLocation = shepherd.zcashParamsDir + '/test'; + const dlLocation = shepherd.zcashParamsDir; const dlOption = req.query.dloption; const successObj = { @@ -3661,8 +3351,8 @@ shepherd.get('/zcparamsdl', (req, res, next) => { * */ shepherd.get('/coinslist', (req, res, next) => { - if (fs.existsSync(`${agamaDir}/shepherd/coinslist.json`)) { - fs.readFile(`${agamaDir}/shepherd/coinslist.json`, 'utf8', (err, data) => { + if (shepherd.fs.existsSync(`${shepherd.agamaDir}/shepherd/coinslist.json`)) { + shepherd.fs.readFile(`${shepherd.agamaDir}/shepherd/coinslist.json`, 'utf8', (err, data) => { if (err) { const errorObj = { msg: 'error', @@ -3694,17 +3384,17 @@ shepherd.get('/coinslist', (req, res, next) => { * params: payload */ shepherd.post('/guilog', (req, res, next) => { - const logLocation = `${agamaDir}/shepherd`; + const logLocation = `${shepherd.agamaDir}/shepherd`; - if (!guiLog[shepherd.appSessionHash]) { - guiLog[shepherd.appSessionHash] = {}; + if (!shepherd.guiLog[shepherd.appSessionHash]) { + shepherd.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; + if (shepherd.guiLog[shepherd.appSessionHash][req.body.timestamp]) { + shepherd.guiLog[shepherd.appSessionHash][req.body.timestamp].status = req.body.status; + shepherd.guiLog[shepherd.appSessionHash][req.body.timestamp].response = req.body.response; } else { - guiLog[shepherd.appSessionHash][req.body.timestamp] = { + shepherd.guiLog[shepherd.appSessionHash][req.body.timestamp] = { function: req.body.function, type: req.body.type, url: req.body.url, @@ -3713,7 +3403,7 @@ shepherd.post('/guilog', (req, res, next) => { }; } - fs.writeFile(`${logLocation}/agamalog.json`, JSON.stringify(guiLog), (err) => { + shepherd.fs.writeFile(`${logLocation}/agamalog.json`, JSON.stringify(shepherd.guiLog), (err) => { if (err) { shepherd.writeLog('error writing gui log file'); } @@ -3734,8 +3424,8 @@ shepherd.post('/guilog', (req, res, next) => { shepherd.get('/getlog', (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', (err, data) => { + if (shepherd.fs.existsSync(`${shepherd.agamaDir}/shepherd/agamalog.${logExt}`)) { + shepherd.fs.readFile(`${shepherd.agamaDir}/shepherd/agamalog.${logExt}`, 'utf8', (err, data) => { if (err) { const errorObj = { msg: 'error', @@ -3777,7 +3467,7 @@ shepherd.post('/coinslist', (req, res, next) => { res.end(JSON.stringify(errorObj)); } else { - fs.writeFile(`${agamaDir}/shepherd/coinslist.json`, JSON.stringify(_payload), (err) => { + shepherd.fs.writeFile(`${shepherd.agamaDir}/shepherd/coinslist.json`, JSON.stringify(_payload), (err) => { if (err) { const errorObj = { msg: 'error', @@ -3802,18 +3492,18 @@ shepherd.quitKomodod = (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; + shepherd.lockDownAddCoin = true; - for (let key in coindInstanceRegistry) { + for (let key in shepherd.coindInstanceRegistry) { const chain = key !== 'komodod' ? key : null; - let _coindQuitCmd = komodocliBin; + let _coindQuitCmd = shepherd.komodocliBin; // any coind if (shepherd.nativeCoindList[key.toLowerCase()]) { - _coindQuitCmd = `${coindRootDir}/${key.toLowerCase()}/${shepherd.nativeCoindList[key.toLowerCase()].bin.toLowerCase()}-cli`; + _coindQuitCmd = `${shepherd.coindRootDir}/${key.toLowerCase()}/${shepherd.nativeCoindList[key.toLowerCase()].bin.toLowerCase()}-cli`; } if (key === 'CHIPS') { - _coindQuitCmd = chipscliBin; + _coindQuitCmd = shepherd.chipscliBin; } const execCliStop = () => { @@ -3829,7 +3519,7 @@ shepherd.quitKomodod = (timeout = 100) => { } _arg.push('stop'); - execFile(`${_coindQuitCmd}`, _arg, (error, stdout, stderr) => { + shepherd.execFile(`${_coindQuitCmd}`, _arg, (error, stdout, stderr) => { shepherd.log(`stdout: ${stdout}`); shepherd.log(`stderr: ${stderr}`); @@ -3838,17 +3528,17 @@ shepherd.quitKomodod = (timeout = 100) => { (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]; + delete shepherd.coindInstanceRegistry[key]; clearInterval(coindExitInterval[key]); } // workaround for AGT-65 - const _port = assetChainPorts[key]; + const _port = shepherd.assetChainPorts[key]; setTimeout(() => { - portscanner.checkPortStatus(_port, '127.0.0.1', (error, status) => { + shepherd.portscanner.checkPortStatus(_port, '127.0.0.1', (error, status) => { // Status is 'open' if currently in use or 'closed' if available if (status === 'closed') { - delete coindInstanceRegistry[key]; + delete shepherd.coindInstanceRegistry[key]; clearInterval(coindExitInterval[key]); } }); @@ -3878,21 +3568,21 @@ shepherd.quitKomodod = (timeout = 100) => { } shepherd.getConf = (chain) => { - let _confLocation = chain === 'komodod' ? `${komodoDir}/komodo.conf` : `${komodoDir}/${chain}/${chain}.conf`; - _confLocation = chain === 'CHIPS' ? `${chipsDir}/chips.conf` : _confLocation; + let _confLocation = chain === 'komodod' ? `${shepherd.komodoDir}/komodo.conf` : `${shepherd.komodoDir}/${chain}/${chain}.conf`; + _confLocation = chain === 'CHIPS' ? `${shepherd.chipsDir}/chips.conf` : _confLocation; // any coind if (chain) { if (shepherd.nativeCoindList[chain.toLowerCase()]) { - const _osHome = os.platform === 'win32' ? process.env.APPDATA : process.env.HOME; + const _osHome = shepherd.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'); + if (shepherd.fs.existsSync(_confLocation)) { + let _port = shepherd.assetChainPorts[chain]; + const _rpcConf = shepherd.fs.readFileSync(_confLocation, 'utf8'); // any coind if (shepherd.nativeCoindList[chain.toLowerCase()]) { @@ -3917,9 +3607,9 @@ shepherd.getConf = (chain) => { } if (shepherd.nativeCoindList[chain.toLowerCase()]) { - rpcConf[chain] = parsedRpcConfig; + shepherd.rpcConf[chain] = parsedRpcConfig; } else { - rpcConf[chain === 'komodod' ? 'KMD' : chain] = parsedRpcConfig; + shepherd.rpcConf[chain === 'komodod' ? 'KMD' : chain] = parsedRpcConfig; } } else { shepherd.log(`${_confLocation} is empty`); @@ -3955,7 +3645,7 @@ shepherd.post('/cli', (req, res, next) => { let _cmd = req.body.payload.cmd; const _params = req.body.payload.params ? ` ${req.body.payload.params}` : ''; - if (!rpcConf[_chain]) { + if (!shepherd.rpcConf[_chain]) { shepherd.getConf(req.body.payload.chain === 'KMD' || !req.body.payload.chain && shepherd.kmdMainPassiveMode ? 'komodod' : req.body.payload.chain); } @@ -3985,7 +3675,7 @@ shepherd.post('/cli', (req, res, next) => { // send back body on both success and error // this bit replicates iguana core's behaviour - request(options, function(error, response, body) { + shepherd.request(options, function(error, response, body) { if (response && response.statusCode && response.statusCode === 200) { @@ -3997,11 +3687,11 @@ shepherd.post('/cli', (req, res, next) => { if (_cmd === 'debug' && _chain !== 'CHIPS') { if (shepherd.nativeCoindList[_chain.toLowerCase()]) { - const _osHome = os.platform === 'win32' ? process.env.APPDATA : process.env.HOME; + const _osHome = shepherd.os.platform === 'win32' ? process.env.APPDATA : process.env.HOME; let coindDebugLogLocation; if (_chain === 'CHIPS') { - coindDebugLogLocation = `${chipsDir}/debug.log`; + coindDebugLogLocation = `${shepherd.chipsDir}/debug.log`; } else { coindDebugLogLocation = `${_osHome}/.${shepherd.nativeCoindList[_chain.toLowerCase()].bin.toLowerCase()}/debug.log`; } @@ -4053,18 +3743,18 @@ shepherd.post('/cli', (req, res, next) => { if (req.body.payload.chain) { const options = { - url: `http://localhost:${rpcConf[req.body.payload.chain].port}`, + url: `http://localhost:${shepherd.rpcConf[req.body.payload.chain].port}`, method: 'POST', auth: { - 'user': rpcConf[req.body.payload.chain].user, - 'pass': rpcConf[req.body.payload.chain].pass + 'user': shepherd.rpcConf[req.body.payload.chain].user, + 'pass': shepherd.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, (error, response, body) => { + shepherd.request(options, (error, response, body) => { if (response && response.statusCode && response.statusCode === 200) { @@ -4076,12 +3766,12 @@ shepherd.post('/cli', (req, res, next) => { } } } else { - let _coindCliBin = komodocliBin; + let _coindCliBin = shepherd.komodocliBin; if (shepherd.nativeCoindList && _chain && shepherd.nativeCoindList[_chain.toLowerCase()]) { - _coindCliBin = `${coindRootDir}/${_chain.toLowerCase()}/${shepherd.nativeCoindList[_chain.toLowerCase()].bin.toLowerCase()}-cli`; + _coindCliBin = `${shepherd.coindRootDir}/${_chain.toLowerCase()}/${shepherd.nativeCoindList[_chain.toLowerCase()].bin.toLowerCase()}-cli`; } let _arg = (_chain ? ' -ac_name=' + _chain : '') + ' ' + _cmd + _params; @@ -4091,7 +3781,7 @@ shepherd.post('/cli', (req, res, next) => { } _arg = _arg.trim().split(' '); - execFile(_coindCliBin, _arg, (error, stdout, stderr) => { + shepherd.execFile(_coindCliBin, _arg, (error, stdout, stderr) => { shepherd.log(`stdout: ${stdout}`); shepherd.log(`stderr: ${stderr}`); @@ -4159,13 +3849,13 @@ shepherd.post('/appconf/reset', (req, res, next) => { res.end(JSON.stringify(successObj)); }); -shepherd.log(`agama dir: ${agamaDir}`); +shepherd.log(`agama dir: ${shepherd.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}`); +shepherd.log(`komodo dir: ${shepherd.komododBin}`); +shepherd.log(`komodo bin: ${shepherd.komodoDir}`); +shepherd.writeLog(`agama dir: ${shepherd.agamaDir}`); +shepherd.writeLog(`komodo dir: ${shepherd.komododBin}`); +shepherd.writeLog(`komodo bin: ${shepherd.komodoDir}`); // default route shepherd.get('/', (req, res, next) => { @@ -4217,20 +3907,20 @@ shepherd.get('/InstantDEX/allcoins', (req, res, next) => { let nativeCoindList = []; let electrumCoinsList = []; - for (let key in electrumCoins) { + for (let key in shepherd.electrumCoins) { if (key !== 'auth') { - electrumCoinsList.push(electrumCoins[key].abbr); + electrumCoinsList.push(shepherd.electrumCoins[key].abbr); } } - for (let key in coindInstanceRegistry) { + for (let key in shepherd.coindInstanceRegistry) { nativeCoindList.push(key === 'komodod' ? 'KMD' : key); } successObj = { native: nativeCoindList, spv: electrumCoinsList, - total: Object.keys(electrumCoins).length - 1 + Object.keys(nativeCoindList).length, + total: Object.keys(shepherd.electrumCoins).length - 1 + Object.keys(nativeCoindList).length, }; res.end(JSON.stringify(successObj)); @@ -4244,15 +3934,15 @@ shepherd.get('/auth/status', (req, res, next) => { // not finished let successObj; let _status = false; - if (Object.keys(coindInstanceRegistry).length) { - if (Object.keys(electrumCoins).length > 1 && electrumCoins.auth) { + if (Object.keys(shepherd.coindInstanceRegistry).length) { + if (Object.keys(shepherd.electrumCoins).length > 1 && shepherd.electrumCoins.auth) { _status = true; - } else if (Object.keys(electrumCoins).length === 1 && !electrumCoins.auth) { + } else if (Object.keys(shepherd.electrumCoins).length === 1 && !shepherd.electrumCoins.auth) { _status = true; } - } else if (Object.keys(electrumCoins).length > 1 && electrumCoins.auth) { + } else if (Object.keys(shepherd.electrumCoins).length > 1 && shepherd.electrumCoins.auth) { _status = true; - } else if (Object.keys(electrumCoins).length === 1 && !Object.keys(coindInstanceRegistry).length) { + } else if (Object.keys(shepherd.electrumCoins).length === 1 && !Object.keys(shepherd.coindInstanceRegistry).length) { _status = true; } @@ -4277,28 +3967,28 @@ shepherd.post('/debuglog', (req, res) => { 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 (shepherd.os.platform() === 'darwin') { + shepherd.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 (shepherd.os.platform() === 'linux') { + shepherd.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 (shepherd.os.platform() === 'win32') { + shepherd.komodoDir = shepherd.appConfig.dataDir.length ? shepherd.appConfig.dataDir : `${process.env.APPDATA}/Komodo`; + shepherd.komodoDir = shepherd.path.normalize(shepherd.komodoDir); } if (_herd === 'komodo') { - _location = komodoDir; + _location = shepherd.komodoDir; } if (_ac) { - _location = `${komodoDir}/${_ac}`; + _location = `${shepherd.komodoDir}/${_ac}`; if (_ac === 'CHIPS') { - _location = chipsDir; + _location = shepherd.chipsDir; } } @@ -4331,10 +4021,10 @@ shepherd.post('/herd', (req, res) => { if (req.body.options && !shepherd.kmdMainPassiveMode) { const testCoindPort = (skipError) => { - if (!lockDownAddCoin) { - const _port = assetChainPorts[req.body.options.ac_name]; + if (!shepherd.lockDownAddCoin) { + const _port = shepherd.assetChainPorts[req.body.options.ac_name]; - portscanner.checkPortStatus(_port, '127.0.0.1', (error, status) => { + shepherd.portscanner.checkPortStatus(_port, '127.0.0.1', (error, status) => { // Status is 'open' if currently in use or 'closed' if available if (status === 'open') { if (!skipError) { @@ -4412,11 +4102,11 @@ shepherd.post('/setconf', (req, res) => { shepherd.log('======= req.body ======='); shepherd.log(req.body); - if (os.platform() === 'win32' && + if (shepherd.os.platform() === 'win32' && req.body.chain == 'komodod') { - setkomodoconf = spawn(path.join(__dirname, '../assets/bin/win64/genkmdconf.bat')); + setkomodoconf = spawn(shepherd.path.join(__dirname, '../assets/bin/win64/genkmdconf.bat')); } else { - setConf(req.body.chain); + shepherd.setConf(req.body.chain); } const obj = { @@ -4567,7 +4257,7 @@ shepherd.get('/kick', (req, res, next) => { } });*/ } else if (currentKickItem.type === 'pattern') { - let dirItems = fs.readdirSync(`${iguanaDir}/currentKickItem.name.replace('[coin]', _coin)`); + let dirItems = shepherd.fs.readdirSync(`${iguanaDir}/currentKickItem.name.replace('[coin]', _coin)`); if (dirItems && dirItems.length) { @@ -4597,18 +4287,18 @@ shepherd.get('/kick', (req, res, next) => { }); shepherd.readDebugLog = (fileLocation, lastNLines) => { - return new Promise( + return new shepherd.Promise( (resolve, reject) => { if (lastNLines) { try { - _fs.access(fileLocation, fs.constants.R_OK, (err) => { + shepherd._fs.access(fileLocation, shepherd.fs.constants.R_OK, (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', (err, data) => { + shepherd._fs.readFile(fileLocation, 'utf-8', (err, data) => { if (err) { shepherd.writeLog(`readDebugLog err: ${err}`); shepherd.log(`readDebugLog err: ${err}`); @@ -4645,7 +4335,7 @@ const herder = (flock, data, coind) => { // 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'; + let kmdDebugLogLocation = (data.ac_name !== 'komodod' ? `${shepherd.komodoDir}/${data.ac_name}` : shepherd.komodoDir) + '/debug.log'; shepherd.log('komodod flock selected...'); shepherd.log(`selected data: ${JSON.stringify(data, null, '\t')}`); @@ -4655,19 +4345,19 @@ const herder = (flock, data, coind) => { // datadir case, check if komodo/chain folder exists if (shepherd.appConfig.dataDir.length && data.ac_name !== 'komodod') { - const _dir = data.ac_name !== 'komodod' ? `${komodoDir}/${data.ac_name}` : komodoDir; + const _dir = data.ac_name !== 'komodod' ? `${shepherd.komodoDir}/${data.ac_name}` : shepherd.komodoDir; try { - _fs.accessSync(_dir, fs.R_OK | fs.W_OK); + shepherd._fs.accessSync(_dir, shepherd.fs.R_OK | shepherd.fs.W_OK); shepherd.log(`komodod datadir ${_dir} exists`); } catch (e) { shepherd.log(`komodod datadir ${_dir} access err: ${e}`); shepherd.log(`attempting to create komodod datadir ${_dir}`); - fs.mkdirSync(_dir); + shepherd.fs.mkdirSync(_dir); - if (fs.existsSync(_dir)) { + if (shepherd.fs.existsSync(_dir)) { shepherd.log(`created komodod datadir folder at ${_dir}`); } else { shepherd.log(`unable to create komodod datadir folder at ${_dir}`); @@ -4678,14 +4368,14 @@ const herder = (flock, data, coind) => { // truncate debug.log if (!shepherd.kmdMainPassiveMode) { try { - const _confFileAccess = _fs.accessSync(kmdDebugLogLocation, fs.R_OK | fs.W_OK); + const _confFileAccess = shepherd._fs.accessSync(kmdDebugLogLocation, shepherd.fs.R_OK | shepherd.fs.W_OK); if (_confFileAccess) { shepherd.log(`error accessing ${kmdDebugLogLocation}`); shepherd.writeLog(`error accessing ${kmdDebugLogLocation}`); } else { try { - fs.unlinkSync(kmdDebugLogLocation); + shepherd.fs.unlinkSync(kmdDebugLogLocation); shepherd.log(`truncate ${kmdDebugLogLocation}`); shepherd.writeLog(`truncate ${kmdDebugLogLocation}`); } catch (e) { @@ -4699,11 +4389,11 @@ const herder = (flock, data, coind) => { } // get komodod instance port - const _port = assetChainPorts[data.ac_name]; + const _port = shepherd.assetChainPorts[data.ac_name]; try { // check if komodod instance is already running - portscanner.checkPortStatus(_port, '127.0.0.1', (error, status) => { + shepherd.portscanner.checkPortStatus(_port, '127.0.0.1', (error, status) => { // Status is 'open' if currently in use or 'closed' if available if (status === 'closed') { // start komodod via exec @@ -4728,18 +4418,18 @@ const herder = (flock, data, coind) => { _customParam = _customParam + ' -datadir=' + shepherd.appConfig.dataDir + (data.ac_name !== 'komodod' ? '/' + data.ac_name : ''); } - shepherd.log(`exec ${komododBin} ${data.ac_options.join(' ')}${_customParam}`); - shepherd.writeLog(`exec ${komododBin} ${data.ac_options.join(' ')}${_customParam}`); + shepherd.log(`exec ${shepherd.komododBin} ${data.ac_options.join(' ')}${_customParam}`); + shepherd.writeLog(`exec ${shepherd.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; + shepherd.coindInstanceRegistry[data.ac_name] = true; if (!shepherd.kmdMainPassiveMode) { let _arg = `${coindACParam}${data.ac_options.join(' ')}${_customParam}`; _arg = _arg.trim().split(' '); - execFile(`${komododBin}`, _arg, { + shepherd.execFile(`${shepherd.komododBin}`, _arg, { maxBuffer: 1024 * 1000000 // 1000 mb }, (error, stdout, stderr) => { shepherd.writeLog(`stdout: ${stdout}`); @@ -4761,7 +4451,7 @@ const herder = (flock, data, coind) => { } } else { if (shepherd.kmdMainPassiveMode) { - coindInstanceRegistry[data.ac_name] = true; + shepherd.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`); @@ -4775,23 +4465,23 @@ const herder = (flock, data, coind) => { // TODO: refactor if (flock === 'chipsd') { - let kmdDebugLogLocation = chipsDir + '/debug.log'; + let kmdDebugLogLocation = `${shepherd.chipsDir}/debug.log`; shepherd.log('chipsd flock selected...'); - shepherd.log('selected data: ' + JSON.stringify(data, null, '\t')); + 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); + const _confFileAccess = shepherd._fs.accessSync(kmdDebugLogLocation, shepherd.fs.R_OK | shepherd.fs.W_OK); if (_confFileAccess) { shepherd.log(`error accessing ${kmdDebugLogLocation}`); shepherd.writeLog(`error accessing ${kmdDebugLogLocation}`); } else { try { - fs.unlinkSync(kmdDebugLogLocation); + shepherd.fs.unlinkSync(kmdDebugLogLocation); shepherd.log(`truncate ${kmdDebugLogLocation}`); shepherd.writeLog(`truncate ${kmdDebugLogLocation}`); } catch (e) { @@ -4804,11 +4494,11 @@ const herder = (flock, data, coind) => { } // get komodod instance port - const _port = assetChainPorts.chipsd; + const _port = shepherd.assetChainPorts.chipsd; try { // check if komodod instance is already running - portscanner.checkPortStatus(_port, '127.0.0.1', (error, status) => { + shepherd.portscanner.checkPortStatus(_port, '127.0.0.1', (error, status) => { // Status is 'open' if currently in use or 'closed' if available if (status === 'closed') { // start komodod via exec @@ -4828,18 +4518,18 @@ const herder = (flock, data, coind) => { _customParam = ` ${_customParamDict[data.ac_custom_param]}${data.ac_custom_param_value}`; } - shepherd.log(`exec ${chipsBin} ${_customParam}`); - shepherd.writeLog(`exec ${chipsBin} ${_customParam}`); + shepherd.log(`exec ${shepherd.chipsBin} ${_customParam}`); + shepherd.writeLog(`exec ${shepherd.chipsBin} ${_customParam}`); shepherd.log(`daemon param ${data.ac_custom_param}`); - coindInstanceRegistry['CHIPS'] = true; + shepherd.coindInstanceRegistry['CHIPS'] = true; let _arg = `${_customParam}`; _arg = _arg.trim().split(' '); if (_arg && _arg.length > 1) { - execFile(`${chipsBin}`, _arg, { + shepherd.execFile(`${shepherd.chipsBin}`, _arg, { maxBuffer: 1024 * 1000000 // 1000 mb }, (error, stdout, stderr) => { shepherd.writeLog(`stdout: ${stdout}`); @@ -4859,7 +4549,7 @@ const herder = (flock, data, coind) => { } }); } else { - execFile(`${chipsBin}`, { + shepherd.execFile(`${shepherd.chipsBin}`, { maxBuffer: 1024 * 1000000 // 1000 mb }, (error, stdout, stderr) => { shepherd.writeLog(`stdout: ${stdout}`); @@ -4888,7 +4578,7 @@ const herder = (flock, data, coind) => { } if (flock === 'zcashd') { // TODO: fix(?) - let kmdDebugLogLocation = `${zcashDir}/debug.log`; + let kmdDebugLogLocation = `${shepherd.zcashDir}/debug.log`; shepherd.log('zcashd flock selected...'); shepherd.log(`selected data: ${data}`); @@ -4902,10 +4592,10 @@ const herder = (flock, data, coind) => { } pm2.start({ - script: zcashdBin, // path to binary + script: shepherd.zcashdBin, // path to binary name: data.ac_name, // REVS, USD, EUR etc. exec_mode: 'fork', - cwd: zcashDir, + cwd: shepherd.zcashDir, args: data.ac_options }, function(err, apps) { shepherd.writeLog(`zcashd fork started ${data.ac_name} ${JSON.stringify(data.ac_options)}`); @@ -4921,7 +4611,7 @@ const herder = (flock, data, coind) => { } if (flock === 'coind') { - const _osHome = os.platform === 'win32' ? process.env.APPDATA : process.env.HOME; + const _osHome = shepherd.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...`); @@ -4931,14 +4621,14 @@ const herder = (flock, data, coind) => { // truncate debug.log try { - _fs.access(coindDebugLogLocation, fs.constants.R_OK, (err) => { + shepherd._fs.access(coindDebugLogLocation, shepherd.fs.constants.R_OK, (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); + shepherd.fs.unlink(coindDebugLogLocation); } }); } catch(e) { @@ -4948,20 +4638,20 @@ const herder = (flock, data, coind) => { // get komodod instance port const _port = shepherd.nativeCoindList[coind.toLowerCase()].port; - const coindBin = `${coindRootDir}/${coind.toLowerCase()}/${shepherd.nativeCoindList[coind.toLowerCase()].bin.toLowerCase()}d`; + const coindBin = `${shepherd.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', (error, status) => { + shepherd.portscanner.checkPortStatus(_port, '127.0.0.1', (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; + shepherd.coindInstanceRegistry[coind] = true; let _arg = `${data.ac_options.join(' ')}`; _arg = _arg.trim().split(' '); - execFile(`${coindBin}`, _arg, { + shepherd.execFile(`${coindBin}`, _arg, { maxBuffer: 1024 * 1000000 // 1000 mb }, (error, stdout, stderr) => { shepherd.writeLog(`stdout: ${stdout}`); @@ -4986,18 +4676,18 @@ const herder = (flock, data, coind) => { shepherd.setConfKMD = (isChips) => { // check if kmd conf exists - _fs.access(isChips ? `${chipsDir}/chips.conf` : `${komodoDir}/komodo.conf`, fs.constants.R_OK, (err) => { + shepherd._fs.access(isChips ? `${shepherd.chipsDir}/chips.conf` : `${shepherd.komodoDir}/komodo.conf`, shepherd.fs.constants.R_OK, (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'); + shepherd.writeLog(isChips ? `creating chips conf in ${shepherd.chipsDir}/chips.conf` : `creating komodo conf in ${shepherd.komodoDir}/komodo.conf`); + shepherd.setConf(isChips ? 'chipsd' : 'komodod'); } else { - const _confSize = fs.lstatSync(isChips ? `${chipsDir}/chips.conf` : `${komodoDir}/komodo.conf`); + const _confSize = shepherd.fs.lstatSync(isChips ? `${shepherd.chipsDir}/chips.conf` : `${shepherd.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'); + shepherd.writeLog(isChips ? `creating chips conf in ${shepherd.chipsDir}/chips.conf` : `creating komodo conf in ${shepherd.komodoDir}/komodo.conf`); + shepherd.setConf(isChips ? 'chipsd' : 'komodod'); } else { shepherd.writeLog(isChips ? 'chips conf exists' : 'komodo conf exists'); shepherd.log(isChips ? 'chips conf exists' : 'komodo conf exists'); @@ -5013,52 +4703,52 @@ const setConf = (flock, coind) => { shepherd.log(flock); shepherd.writeLog(`setconf ${flock}`); - if (os.platform() === 'darwin') { + if (shepherd.os.platform() === 'darwin') { nativeCoindDir = coind ? `${process.env.HOME}/Library/Application Support/${shepherd.nativeCoindList[coind.toLowerCase()].bin}` : null; } - if (os.platform() === 'linux') { + if (shepherd.os.platform() === 'linux') { nativeCoindDir = coind ? `${process.env.HOME}/.${shepherd.nativeCoindList[coind.toLowerCase()].bin.toLowerCase()}` : null; } - if (os.platform() === 'win32') { + if (shepherd.os.platform() === 'win32') { nativeCoindDir = coind ? `${process.env.APPDATA}/${shepherd.nativeCoindList[coind.toLowerCase()].bin}` : null; } switch (flock) { case 'komodod': - DaemonConfPath = `${komodoDir}/komodo.conf`; + DaemonConfPath = `${shepherd.komodoDir}/komodo.conf`; - if (os.platform() === 'win32') { - DaemonConfPath = path.normalize(DaemonConfPath); + if (shepherd.os.platform() === 'win32') { + DaemonConfPath = shepherd.path.normalize(DaemonConfPath); } break; case 'zcashd': - DaemonConfPath = `${ZcashDir}/zcash.conf`; + DaemonConfPath = `${shepherd.ZcashDir}/zcash.conf`; - if (os.platform() === 'win32') { - DaemonConfPath = path.normalize(DaemonConfPath); + if (shepherd.os.platform() === 'win32') { + DaemonConfPath = shepherd.path.normalize(DaemonConfPath); } break; case 'chipsd': - DaemonConfPath = `${chipsDir}/chips.conf`; + DaemonConfPath = `${shepherd.chipsDir}/chips.conf`; - if (os.platform() === 'win32') { - DaemonConfPath = path.normalize(DaemonConfPath); + if (shepherd.os.platform() === 'win32') { + DaemonConfPath = shepherd.path.normalize(DaemonConfPath); } break; case 'coind': DaemonConfPath = `${nativeCoindDir}/${shepherd.nativeCoindList[coind.toLowerCase()].bin.toLowerCase()}.conf`; - if (os.platform() === 'win32') { - DaemonConfPath = path.normalize(DaemonConfPath); + if (shepherd.os.platform() === 'win32') { + DaemonConfPath = shepherd.path.normalize(DaemonConfPath); } break; default: - DaemonConfPath = `${komodoDir}/${flock}/${flock}.conf`; + DaemonConfPath = `${shepherd.komodoDir}/${flock}/${flock}.conf`; - if (os.platform() === 'win32') { - DaemonConfPath = path.normalize(DaemonConfPath); + if (shepherd.os.platform() === 'win32') { + DaemonConfPath = shepherd.path.normalize(DaemonConfPath); } } @@ -5066,10 +4756,10 @@ const setConf = (flock, coind) => { shepherd.writeLog(`setconf ${DaemonConfPath}`); const CheckFileExists = () => { - return new Promise((resolve, reject) => { + return new shepherd.Promise((resolve, reject) => { const result = 'Check Conf file exists is done'; - const confFileExist = fs.ensureFileSync(DaemonConfPath); + const confFileExist = shepherd.fs.ensureFileSync(DaemonConfPath); if (confFileExist) { shepherd.log(result); shepherd.writeLog(`setconf ${result}`); @@ -5083,10 +4773,10 @@ const setConf = (flock, coind) => { } const FixFilePermissions = () => { - return new Promise((resolve, reject) => { + return new shepherd.Promise((resolve, reject) => { const result = 'Conf file permissions updated to Read/Write'; - fsnode.chmodSync(DaemonConfPath, '0666'); + shepherd.fsnode.chmodSync(DaemonConfPath, '0666'); shepherd.log(result); shepherd.writeLog(`setconf ${result}`); @@ -5095,10 +4785,10 @@ const setConf = (flock, coind) => { } const RemoveLines = () => { - return new Promise((resolve, reject) => { + return new shepherd.Promise((resolve, reject) => { const result = 'RemoveLines is done'; - fs.readFile(DaemonConfPath, 'utf8', (err, data) => { + shepherd.fs.readFile(DaemonConfPath, 'utf8', (err, data) => { if (err) { shepherd.writeLog(`setconf error ${err}`); return shepherd.log(err); @@ -5106,11 +4796,11 @@ const setConf = (flock, coind) => { const rmlines = data.replace(/(?:(?:\r\n|\r|\n)\s*){2}/gm, '\n'); - fs.writeFile(DaemonConfPath, rmlines, 'utf8', (err) => { + shepherd.fs.writeFile(DaemonConfPath, rmlines, 'utf8', (err) => { if (err) return shepherd.log(err); - fsnode.chmodSync(DaemonConfPath, '0666'); + shepherd.fsnode.chmodSync(DaemonConfPath, '0666'); shepherd.writeLog(`setconf ${result}`); shepherd.log(result); resolve(result); @@ -5120,12 +4810,12 @@ const setConf = (flock, coind) => { } const CheckConf = () => { - return new Promise((resolve, reject) => { + return new shepherd.Promise((resolve, reject) => { const result = 'CheckConf is done'; - setconf.status(DaemonConfPath, (err, status) => { + shepherd.setconf.status(DaemonConfPath, (err, status) => { const rpcuser = () => { - return new Promise((resolve, reject) => { + return new shepherd.Promise((resolve, reject) => { const result = 'checking rpcuser...'; if (status[0].hasOwnProperty('rpcuser')) { @@ -5137,7 +4827,7 @@ const setConf = (flock, coind) => { shepherd.log('rpcuser: NOT FOUND'); shepherd.writeLog('rpcuser: NOT FOUND'); - fs.appendFile(DaemonConfPath, `\nrpcuser=user${randomstring.substring(0, 16)}`, (err) => { + shepherd.fs.appendFile(DaemonConfPath, `\nrpcuser=user${randomstring.substring(0, 16)}`, (err) => { if (err) { shepherd.writeLog(`append daemon conf err: ${err}`); shepherd.log(`append daemon conf err: ${err}`); @@ -5153,7 +4843,7 @@ const setConf = (flock, coind) => { } const rpcpass = () => { - return new Promise((resolve, reject) => { + return new shepherd.Promise((resolve, reject) => { const result = 'checking rpcpassword...'; if (status[0].hasOwnProperty('rpcpassword')) { @@ -5165,7 +4855,7 @@ const setConf = (flock, coind) => { shepherd.log('rpcpassword: NOT FOUND'); shepherd.writeLog('rpcpassword: NOT FOUND'); - fs.appendFile(DaemonConfPath, `\nrpcpassword=${randomstring}`, (err) => { + shepherd.fs.appendFile(DaemonConfPath, `\nrpcpassword=${randomstring}`, (err) => { if (err) { shepherd.writeLog(`append daemon conf err: ${err}`); shepherd.log(`append daemon conf err: ${err}`); @@ -5181,7 +4871,7 @@ const setConf = (flock, coind) => { } const rpcbind = () => { - return new Promise((resolve, reject) => { + return new shepherd.Promise((resolve, reject) => { const result = 'checking rpcbind...'; if (status[0].hasOwnProperty('rpcbind')) { @@ -5191,7 +4881,7 @@ const setConf = (flock, coind) => { shepherd.log('rpcbind: NOT FOUND'); shepherd.writeLog('rpcbind: NOT FOUND'); - fs.appendFile(DaemonConfPath, '\nrpcbind=127.0.0.1', (err) => { + shepherd.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}`); @@ -5207,7 +4897,7 @@ const setConf = (flock, coind) => { } const server = () => { - return new Promise((resolve, reject) => { + return new shepherd.Promise((resolve, reject) => { const result = 'checking server...'; if (status[0].hasOwnProperty('server')) { @@ -5217,7 +4907,7 @@ const setConf = (flock, coind) => { shepherd.log('server: NOT FOUND'); shepherd.writeLog('server: NOT FOUND'); - fs.appendFile(DaemonConfPath, '\nserver=1', (err) => { + shepherd.fs.appendFile(DaemonConfPath, '\nserver=1', (err) => { if (err) { shepherd.writeLog(`append daemon conf err: ${err}`); shepherd.log(`append daemon conf err: ${err}`); @@ -5233,7 +4923,7 @@ const setConf = (flock, coind) => { } const addnode = () => { - return new Promise((resolve, reject) => { + return new shepherd.Promise((resolve, reject) => { const result = 'checking addnode...'; if (flock === 'chipsd' || @@ -5266,7 +4956,7 @@ const setConf = (flock, coind) => { } shepherd.log('addnode: NOT FOUND'); - fs.appendFile(DaemonConfPath, nodesList, (err) => { + shepherd.fs.appendFile(DaemonConfPath, nodesList, (err) => { if (err) { shepherd.writeLog(`append daemon conf err: ${err}`); shepherd.log(`append daemon conf err: ${err}`); @@ -5309,8 +4999,8 @@ const setConf = (flock, coind) => { } shepherd.getMaxconKMDConf = () => { - return new Promise((resolve, reject) => { - fs.readFile(`${komodoDir}/komodo.conf`, 'utf8', (err, data) => { + return new shepherd.Promise((resolve, reject) => { + shepherd.fs.readFile(`${shepherd.komodoDir}/komodo.conf`, 'utf8', (err, data) => { if (err) { shepherd.log(`kmd conf maxconnections param read failed`); resolve('unset'); @@ -5330,12 +5020,12 @@ shepherd.getMaxconKMDConf = () => { } shepherd.setMaxconKMDConf = (limit) => { - return new Promise((resolve, reject) => { - fs.readFile(`${komodoDir}/komodo.conf`, 'utf8', (err, data) => { + return new shepherd.Promise((resolve, reject) => { + shepherd.fs.readFile(`${shepherd.komodoDir}/komodo.conf`, 'utf8', (err, data) => { const _maxconVal = limit ? 1 : 10; if (err) { - shepherd.log(`error reading ${komodoDir}/komodo.conf`); + shepherd.log(`error reading ${shepherd.komodoDir}/komodo.conf`); resolve(false); } else { if (data.indexOf('maxconnections=') > -1) { @@ -5346,9 +5036,9 @@ shepherd.setMaxconKMDConf = (limit) => { data = `${data}\nmaxconnections=${_maxconVal}\n`; } - fs.writeFile(`${komodoDir}/komodo.conf`, data, (err) => { + shepherd.fs.writeFile(`${shepherd.komodoDir}/komodo.conf`, data, (err) => { if (err) { - shepherd.log(`error writing ${komodoDir}/komodo.conf maxconnections=${_maxconVal}`); + shepherd.log(`error writing ${shepherd.komodoDir}/komodo.conf maxconnections=${_maxconVal}`); resolve(false); } else { shepherd.log(`kmd conf maxconnections is set to ${_maxconVal}`); @@ -5372,7 +5062,7 @@ const getConf = (flock, coind) => { shepherd.log(`getconf coind ${coind}`); shepherd.writeLog(`getconf flock: ${flock}`); - switch (os.platform()) { + switch (shepherd.os.platform()) { case 'darwin': nativeCoindDir = `${process.env.HOME}/Library/Application Support/${shepherd.nativeCoindList[coind.toLowerCase()].bin}`; break; @@ -5386,31 +5076,31 @@ const getConf = (flock, coind) => { switch (flock) { case 'komodod': - DaemonConfPath = komodoDir; - if (os.platform() === 'win32') { - DaemonConfPath = path.normalize(DaemonConfPath); + DaemonConfPath = shepherd.komodoDir; + if (shepherd.os.platform() === 'win32') { + DaemonConfPath = shepherd.path.normalize(DaemonConfPath); shepherd.log('===>>> SHEPHERD API OUTPUT ===>>>'); } break; case 'zcashd': - DaemonConfPath = ZcashDir; - if (os.platform() === 'win32') { - DaemonConfPath = path.normalize(DaemonConfPath); + DaemonConfPath = shepherd.ZcashDir; + if (shepherd.os.platform() === 'win32') { + DaemonConfPath = shepherd.path.normalize(DaemonConfPath); } break; case 'chipsd': - DaemonConfPath = chipsDir; - if (os.platform() === 'win32') { - DaemonConfPath = path.normalize(DaemonConfPath); + DaemonConfPath = shepherd.chipsDir; + if (shepherd.os.platform() === 'win32') { + DaemonConfPath = shepherd.path.normalize(DaemonConfPath); } break; case 'coind': - DaemonConfPath = os.platform() === 'win32' ? path.normalize(`${coindRootDir}/${coind.toLowerCase()}`) : `${coindRootDir}/${coind.toLowerCase()}`; + DaemonConfPath = shepherd.os.platform() === 'win32' ? shepherd.path.normalize(`${shepherd.coindRootDir}/${coind.toLowerCase()}`) : `${shepherd.coindRootDir}/${coind.toLowerCase()}`; break; default: - DaemonConfPath = `${komodoDir}/${flock}`; - if (os.platform() === 'win32') { - DaemonConfPath = path.normalize(DaemonConfPath); + DaemonConfPath = `${shepherd.komodoDir}/${flock}`; + if (shepherd.os.platform() === 'win32') { + DaemonConfPath = shepherd.path.normalize(DaemonConfPath); } } @@ -5421,8 +5111,9 @@ const getConf = (flock, coind) => { } const formatBytes = (bytes, decimals) => { - if (bytes === 0) + if (bytes === 0) { return '0 Bytes'; + } const k = 1000; const dm = (decimals + 1) || 3; @@ -5444,14 +5135,14 @@ const formatBytes = (bytes, decimals) => { shepherd.SystemInfo = () => { 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(), + 'totalmem_bytes': shepherd.os.totalmem(), + 'totalmem_readable': formatBytes(shepherd.os.totalmem()), + 'arch': shepherd.os.arch(), + 'cpu': shepherd.os.cpus()[0].model, + 'cpu_cores': shepherd.os.cpus().length, + 'platform': shepherd.os.platform(), + 'os_release': shepherd.os.release(), + 'os_type': shepherd.os.type(), }; return os_data; @@ -5461,11 +5152,11 @@ shepherd.appInfo = () => { const sysInfo = shepherd.SystemInfo(); const releaseInfo = shepherd.appBasicInfo; const dirs = { - agamaDir, - komodoDir, - komododBin, - configLocation: `${agamaDir}/config.json`, - cacheLocation: `${agamaDir}/shepherd`, + agamaDir: shepherd.agamaDir, + komodoDir: shepherd.komodoDir, + komododBin: shepherd.komododBin, + configLocation: `${shepherd.agamaDir}/config.json`, + cacheLocation: `${shepherd.agamaDir}/shepherd`, }; return { From 8cef942e42c76c1d2458b45d3a94dee0233af7ad Mon Sep 17 00:00:00 2001 From: pbca26 Date: Wed, 18 Oct 2017 21:42:17 +0300 Subject: [PATCH 2/3] app closing window info --- gui/startup/app-closing.html | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/gui/startup/app-closing.html b/gui/startup/app-closing.html index 79185d0..87618d3 100644 --- a/gui/startup/app-closing.html +++ b/gui/startup/app-closing.html @@ -22,7 +22,10 @@ alt="Agama Wallet" width="80" height="100" /> -
App is closing. Please wait...
+
+ App is closing. Please wait...

+ This may take a while depending on your system resources and current state of daemon applications. +
From 8867aa8426c6773a1e0d0f9e10c8a19be216f3a2 Mon Sep 17 00:00:00 2001 From: pbca26 Date: Thu, 19 Oct 2017 13:29:10 +0300 Subject: [PATCH 3/3] split shepherd.js into modules --- main.js | 6 +- routes/electrumjs/electrumServers.js | 158 + routes/fetchparams.js | 197 - routes/shepherd.js | 5154 +--------------------- routes/shepherd/addCoinShortcuts.js | 103 + routes/shepherd/appInfo.js | 78 + routes/shepherd/auth.js | 34 + routes/shepherd/binsTestUtil.js | 239 + routes/shepherd/binsUtils.js | 70 + routes/shepherd/coins.js | 31 + routes/shepherd/coinsList.js | 71 + routes/shepherd/confMaxconnections.js | 55 + routes/shepherd/config.js | 152 + routes/shepherd/daemonControl.js | 868 ++++ routes/shepherd/dashboardUpdate.js | 294 ++ routes/shepherd/debugLog.js | 92 + routes/shepherd/dex/coind.js | 44 + routes/shepherd/downloadBins.js | 165 + routes/shepherd/downloadPatch.js | 154 + routes/shepherd/downloadUtil.js | 49 + routes/shepherd/downloadZcparams.js | 127 + routes/shepherd/electrum/auth.js | 38 + routes/shepherd/electrum/balance.js | 145 + routes/shepherd/electrum/block.js | 59 + routes/shepherd/electrum/coins.js | 69 + routes/shepherd/electrum/createtx.js | 411 ++ routes/shepherd/electrum/estimate.js | 26 + routes/shepherd/electrum/interest.js | 37 + routes/shepherd/electrum/keys.js | 128 + routes/shepherd/electrum/listunspent.js | 194 + routes/shepherd/electrum/merkle.js | 145 + routes/shepherd/electrum/network.js | 141 + routes/shepherd/electrum/transactions.js | 397 ++ routes/shepherd/init.js | 52 + routes/shepherd/kickstart.js | 150 + routes/shepherd/log.js | 141 + routes/shepherd/paths.js | 74 + routes/shepherd/pin.js | 146 + routes/shepherd/quitDaemon.js | 82 + routes/shepherd/rpc.js | 246 ++ 40 files changed, 5523 insertions(+), 5299 deletions(-) create mode 100644 routes/electrumjs/electrumServers.js delete mode 100644 routes/fetchparams.js create mode 100644 routes/shepherd/addCoinShortcuts.js create mode 100644 routes/shepherd/appInfo.js create mode 100644 routes/shepherd/auth.js create mode 100644 routes/shepherd/binsTestUtil.js create mode 100644 routes/shepherd/binsUtils.js create mode 100644 routes/shepherd/coins.js create mode 100644 routes/shepherd/coinsList.js create mode 100644 routes/shepherd/confMaxconnections.js create mode 100644 routes/shepherd/config.js create mode 100644 routes/shepherd/daemonControl.js create mode 100644 routes/shepherd/dashboardUpdate.js create mode 100644 routes/shepherd/debugLog.js create mode 100644 routes/shepherd/dex/coind.js create mode 100644 routes/shepherd/downloadBins.js create mode 100644 routes/shepherd/downloadPatch.js create mode 100644 routes/shepherd/downloadUtil.js create mode 100644 routes/shepherd/downloadZcparams.js create mode 100644 routes/shepherd/electrum/auth.js create mode 100644 routes/shepherd/electrum/balance.js create mode 100644 routes/shepherd/electrum/block.js create mode 100644 routes/shepherd/electrum/coins.js create mode 100644 routes/shepherd/electrum/createtx.js create mode 100644 routes/shepherd/electrum/estimate.js create mode 100644 routes/shepherd/electrum/interest.js create mode 100644 routes/shepherd/electrum/keys.js create mode 100644 routes/shepherd/electrum/listunspent.js create mode 100644 routes/shepherd/electrum/merkle.js create mode 100644 routes/shepherd/electrum/network.js create mode 100644 routes/shepherd/electrum/transactions.js create mode 100644 routes/shepherd/init.js create mode 100644 routes/shepherd/kickstart.js create mode 100644 routes/shepherd/log.js create mode 100644 routes/shepherd/paths.js create mode 100644 routes/shepherd/pin.js create mode 100644 routes/shepherd/quitDaemon.js create mode 100644 routes/shepherd/rpc.js diff --git a/main.js b/main.js index 4303356..d3757e7 100644 --- a/main.js +++ b/main.js @@ -29,6 +29,9 @@ if (osPlatform === 'linux') { // GUI APP settings and starting gui on address http://120.0.0.1:17777 var shepherd = require('./routes/shepherd'); var guiapp = express(); + +shepherd.createAgamaDirs(); + var appConfig = shepherd.loadLocalConfig(); // load app config const nativeCoindList = shepherd.scanNativeCoindBins(); @@ -51,7 +54,6 @@ const appBasicInfo = { app.setName(appBasicInfo.name); app.setVersion(appBasicInfo.version); -shepherd.binFixRights(); shepherd.createAgamaDirs(); const appSessionHash = md5(Date.now().toString()); @@ -280,7 +282,7 @@ function createAppCloseWindow() { // initialise window appCloseWindow = new BrowserWindow({ // dirty hack to prevent main window flash on quit width: 500, - height: 300, + height: 320, frame: false, icon: agamaIcon, show: false, diff --git a/routes/electrumjs/electrumServers.js b/routes/electrumjs/electrumServers.js new file mode 100644 index 0000000..0f86509 --- /dev/null +++ b/routes/electrumjs/electrumServers.js @@ -0,0 +1,158 @@ +let electrumServers = { + /*zcash: { + 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' + ], + }, +}; + +module.exports = electrumServers; \ No newline at end of file diff --git a/routes/fetchparams.js b/routes/fetchparams.js deleted file mode 100644 index 53fd0c9..0000000 --- a/routes/fetchparams.js +++ /dev/null @@ -1,197 +0,0 @@ -var app = require('http').createServer(handler), - io = require('socket.io')(app), - fs = require('fs'), - request = require('request'), - progress = require('request-progress'); -const path = require('path'), - url = require('url'), - os = require('os'), - sha256 = require('sha256'), - crypto = require('crypto'); - -Promise = require('bluebird'); - -app.listen(3000); - -function handler (req, res) { - fs.readFile(__dirname + '/index.html', - function (err, data) { - if (err) { - res.writeHead(500); - return res.end('Error loading index.html'); - } - - res.writeHead(200); - res.end(data); - }); -} - -if (os.platform() === 'darwin') { - var PARAMS_DIR = process.env.HOME + '/Library/Application Support/ZcashParams'; -} -if (os.platform() === 'linux') { - var PARAMS_DIR = process.env.HOME + '/.zcash-params'; -} - -var SPROUT_FILES_DATA = [ - { - 'file': 'sprout-proving.key', - 'hash': '8bc20a7f013b2b58970cddd2e7ea028975c88ae7ceb9259a5344a16bc2c0eef7' - }, { - 'file': 'sprout-verifying.key', - 'hash': '4bd498dae0aacfd8e98dc306338d017d9c08dd0918ead18172bd0aec2fc5df82' - } -]; -var SPROUT_DL_URL = 'https://z.cash/downloads/'; - -SPROUT_FILES_DATA.forEach(function(value, index) { - fs.exists(value.file, function(exists) { - if (exists) { - console.log(value.file + ' already exists at location.'); - - var tmphas, - fd = fs.createReadStream(value.file), - hash = crypto.createHash('sha256'); - - hash.setEncoding('hex'); - - fd.on('end', function() { - hash.end(); - - console.log('hash is: '); - console.log(hash.read()); // the desired sha1sum - console.log(value.hash); - - tmphash = hash.read(); - if (hash.read() === value.hash) { - console.log('File SHA256 sum matches.'); - } else { - console.log('File SHA256 sum does not match.'); - } - }); - // read all file and pipe it (write it) to the hash object - fd.pipe(hash); - } else { - var DLFile = function() { - return new Promise(function(resolve, reject) { - console.log('file not there.'); - - progress(request(SPROUT_DL_URL + value.file), {}) - .on('progress', function (state) { - console.log('progress', state); - }) - .on('error', function (err) { - console.log(err); - }) - .on('end', function () { - // Do something after request finishes - console.log('download finished.'); - var result = 'File ==> ' + value.file + ': DOWNLOADED'; - }) - .pipe(fs.createWriteStream(value.file)); - - console.log(result); - resolve(result); - }) - } - var CheckFileSHA = function() { - return new Promise(function(resolve, reject) { - var fd = fs.createReadStream(value.file), - hash = crypto.createHash('sha256'); - - hash.setEncoding('hex'); - - fd.on('end', function() { - hash.end(); - - console.log('hash is: '); - console.log(hash.read()); // the desired sha1sum - console.log(value.hash); - - if (hash.read() === value.hash) { - console.log('File SHA256 sum matches.'); - } else { - console.log('File SHA256 sum does not match.'); - } - }); - // read all file and pipe it (write it) to the hash object - fd.pipe(hash); - - var result = 'SHA256 SUM Check: DONE'; - - console.log(result); - resolve(result); - }); - } - - DLFile() - .then(function(result) { - return CheckFileSHA(); - }); - } - }); -}); - -function CheckSHASum(file, hashstr) { - console.log(hashstr); - var shasum; - - // the file you want to get the hash - if (shasum === hashstr ) { - return true; - } else { - return false; - } -} - -/*var CheckFileExists = function() { - - return new Promise(function(resolve, reject) { - - if (path.existsSync('foo.txt')) {} - var result = 'Connecting To Pm2: done' - - console.log(result) - resolve(result); - }) -} - -var DLFile = function() { - - return new Promise(function(resolve, reject) { - var result = 'Killing Pm2: done' - - setTimeout(function() { - console.log(result) - resolve(result); - }, 2000) - }) -} - -var CheckSHASum = function() { - - return new Promise(function(resolve, reject) { - var result = 'Hiding Main Window: done' - - console.log(result) - resolve(result); - }) -} - -var MoveFile = function() { - - return new Promise(function(resolve, reject) { - var result = 'Quiting App: done' - - console.log(result) - resolve(result); - }) -} - -ConnectToPm2() -.then(function(result) { - return KillPm2(); -}) -.then(HideMainWindow) -.then(QuitApp) -*/ \ No newline at end of file diff --git a/routes/shepherd.js b/routes/shepherd.js index 5926c2d..ab258e6 100644 --- a/routes/shepherd.js +++ b/routes/shepherd.js @@ -8,7 +8,7 @@ shepherd.os = require('os'); shepherd.fsnode = require('fs'); shepherd.fs = require('fs-extra'); shepherd._fs = require('graceful-fs'); -// shepherd.md5 = require('./md5.js'); +shepherd.md5 = require('./md5.js'); shepherd.request = require('request'); shepherd.portscanner = require('portscanner'); shepherd.aes256 = require('nodejs-aes256'); @@ -36,6 +36,8 @@ shepherd.rpcConf = {}; shepherd.appRuntimeLog = []; shepherd.appRuntimeSPVLog = []; shepherd.lockDownAddCoin = false; + +// spv vars and libs shepherd.electrumCoins = { auth: false, }; @@ -46,5125 +48,77 @@ shepherd.electrumJSNetworks = require('./electrumjs/electrumjs.networks.js'); shepherd.electrumJSTxDecoder = require('./electrumjs/electrumjs.txdecoder.js'); shepherd.electrumServers = require('./electrumjs/electrumServers.js'); -shepherd.zcashParamsDownloadLinks = { - 'agama.komodoplatform.com': { - proving: 'https://agama.komodoplatform.com/file/supernet/sprout-proving.key', - verifying: 'https://agama.komodoplatform.com/file/supernet/sprout-verifying.key', - }, - 'agama-yq0ysrdtr.stackpathdns.com': { - proving: 'http://agama-yq0ysrdtr.stackpathdns.com/file/supernet/sprout-proving.key', - verifying: 'http://agama-yq0ysrdtr.stackpathdns.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.CONNECTION_ERROR_OR_INCOMPLETE_DATA = 'connection error or incomplete data'; shepherd.appConfig = shepherd._appConfig.config; -shepherd.agamaDir; -switch (shepherd.os.platform()) { - case 'darwin': - shepherd.fixPath(); - shepherd.agamaDir = `${process.env.HOME}/Library/Application Support/Agama`; - break; - - case 'linux': - shepherd.agamaDir = `${process.env.HOME}/.agama`; - break; +shepherd = require('./shepherd/paths.js')(shepherd); - case 'win32': - shepherd.agamaDir = `${process.env.APPDATA}/Agama`; - shepherd.agamaDir = shepherd.path.normalize(shepherd.agamaDir); - break; -} +shepherd.pathsAgama(); -shepherd = require('./log.js')(shepherd); -shepherd = require('./config.js')(shepherd); +shepherd = require('./shepherd/log.js')(shepherd); +shepherd = require('./shepherd/config.js')(shepherd); shepherd.appConfig = shepherd.loadLocalConfig(); -if (shepherd.os.platform() === 'darwin') { - shepherd.fixPath(); - shepherd.agamaTestDir = `${process.env.HOME}/Library/Application Support/Agama/test`, - shepherd.komododBin = shepherd.path.join(__dirname, '../assets/bin/osx/komodod'), - shepherd.komodocliBin = shepherd.path.join(__dirname, '../assets/bin/osx/komodo-cli'), - shepherd.komodoDir = shepherd.appConfig.dataDir.length ? shepherd.appConfig.dataDir : `${process.env.HOME}/Library/Application Support/Komodo`, - shepherd.zcashdBin = '/Applications/ZCashSwingWalletUI.app/Contents/MacOS/zcashd', - shepherd.zcashcliBin = '/Applications/ZCashSwingWalletUI.app/Contents/MacOS/zcash-cli', - shepherd.zcashDir = `${process.env.HOME}/Library/Application Support/Zcash`, - shepherd.zcashParamsDir = `${process.env.HOME}/Library/Application Support/ZcashParams`, - shepherd.chipsBin = shepherd.path.join(__dirname, '../assets/bin/osx/chipsd'), - shepherd.chipscliBin = shepherd.path.join(__dirname, '../assets/bin/osx/chips-cli'), - shepherd.chipsDir = `${process.env.HOME}/Library/Application Support/Chips`, - shepherd.coindRootDir = shepherd.path.join(__dirname, '../assets/bin/osx/dex/coind'); -} - -if (shepherd.os.platform() === 'linux') { - shepherd.agamaTestDir = `${process.env.HOME}/.agama/test`, - shepherd.komododBin = shepherd.path.join(__dirname, '../assets/bin/linux64/komodod'), - shepherd.komodocliBin = shepherd.path.join(__dirname, '../assets/bin/linux64/komodo-cli'), - shepherd.komodoDir = shepherd.appConfig.dataDir.length ? shepherd.appConfig.dataDir : `${process.env.HOME}/.komodo`, - shepherd.zcashParamsDir = `${process.env.HOME}/.zcash-params`, - shepherd.chipsBin = shepherd.path.join(__dirname, '../assets/bin/linux64/chipsd'), - shepherd.chipscliBin = shepherd.path.join(__dirname, '../assets/bin/linux64/chips-cli'), - shepherd.chipsDir = `${process.env.HOME}/.chips`, - shepherd.coindRootDir = shepherd.path.join(__dirname, '../assets/bin/linux64/dex/coind'); -} - -if (shepherd.os.platform() === 'win32') { - shepherd.agamaTestDir = `${process.env.APPDATA}/Agama/test`; - shepherd.agamaTestDir = shepherd.path.normalize(shepherd.agamaTestDir); - shepherd.komododBin = shepherd.path.join(__dirname, '../assets/bin/win64/komodod.exe'), - shepherd.komododBin = shepherd.path.normalize(shepherd.komododBin), - shepherd.komodocliBin = shepherd.path.join(__dirname, '../assets/bin/win64/komodo-cli.exe'), - shepherd.komodocliBin = shepherd.path.normalize(shepherd.komodocliBin), - shepherd.komodoDir = shepherd.appConfig.dataDir.length ? shepherd.appConfig.dataDir : `${process.env.APPDATA}/Komodo`, - shepherd.komodoDir = shepherd.path.normalize(shepherd.komodoDir); - shepherd.chipsBin = shepherd.path.join(__dirname, '../assets/bin/win64/chipsd.exe'), - shepherd.chipsBin = shepherd.path.normalize(shepherd.chipsBin), - shepherd.chipscliBin = shepherd.path.join(__dirname, '../assets/bin/win64/chips-cli.exe'), - shepherd.chipscliBin = shepherd.path.normalize(shepherd.chipscliBin), - shepherd.chipsDir = `${process.env.APPDATA}/Chips`, - shepherd.chipsDir = shepherd.path.normalize(shepherd.chipsDir); - shepherd.zcashParamsDir = `${process.env.APPDATA}/ZcashParams`; - shepherd.zcashParamsDir = shepherd.path.normalize(shepherd.zcashParamsDir); - shepherd.coindRootDir = shepherd.path.join(__dirname, '../assets/bin/osx/dex/coind'); - shepherd.coindRootDir = shepherd.path.normalize(shepherd.coindRootDir); -} +shepherd.pathsDaemons(); shepherd.appConfigSchema = shepherd._appConfig.schema; shepherd.defaultAppConfig = Object.assign({}, shepherd.appConfig); shepherd.kmdMainPassiveMode = false; -shepherd.getNetworkData = (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 shepherd.electrumJSNetworks.komodo; - } else { - return shepherd.electrumJSNetworks[network]; - } -} - -shepherd.seedToWif = (seed, network, iguana) => { - const bytes = shepherd.sha256(seed, { asBytes: true }); - - if (iguana) { - bytes[0] &= 248; - bytes[31] &= 127; - bytes[31] |= 64; - } - - const toHexString = (byteArray) => { - return Array.from(byteArray, (byte) => { - return ('0' + (byte & 0xFF).toString(16)).slice(-2); - }).join(''); - } - - const hex = toHexString(bytes); - - const key = new shepherd.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}`, true); - shepherd.log(`seedtowif pub key ${key.publicAddress}`, true); - - return { - priv: key.privateWif, - pub: key.publicAddress, - }; -} - -shepherd.get('/electrum/seedtowif', (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 = (coin) => { - for (let key in shepherd.electrumServers) { - if (shepherd.electrumServers[key].abbr === coin) { - return key; - } - } -} - -shepherd.findCoinName = (network) => { - for (let key in shepherd.electrumServers) { - if (key === network) { - return shepherd.electrumServers[key].abbr; - } - } -} - -shepherd.get('/electrum/servers/test', (req, res, next) => { - const ecl = new shepherd.electrumJSCore(req.query.port, req.query.address, 'tcp'); // tcp or tls - - ecl.connect(); - ecl.serverVersion() - .then((serverData) => { - ecl.close(); - shepherd.log('serverData', true); - shepherd.log(serverData, true); - - 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', (req, res, next) => { - let _matchingKeyPairs = 0; - let _electrumKeys = {}; - - for (let key in shepherd.electrumServers) { - const _abbr = shepherd.electrumServers[key].abbr; - const { priv, pub } = shepherd.seedToWif(req.query.seed, shepherd.findNetworkObj(_abbr), req.query.iguana); - - if (shepherd.electrumKeys[_abbr].pub === pub && - shepherd.electrumKeys[_abbr].priv === priv) { - _matchingKeyPairs++; - } - } - - if (req.query.active) { - _electrumKeys = JSON.parse(JSON.stringify(shepherd.electrumKeys)); - - for (let key in _electrumKeys) { - if (!shepherd.electrumCoins[key]) { - delete _electrumKeys[key]; - } - } - } else { - _electrumKeys = shepherd.electrumKeys; - } - - shepherd.log(JSON.stringify(_electrumKeys, null, '\t'), true); - - const successObj = { - msg: 'success', - result: _matchingKeyPairs === Object.keys(shepherd.electrumKeys).length ? _electrumKeys : false, - }; - - res.end(JSON.stringify(successObj)); -}); - -shepherd.get('/electrum/login', (req, res, next) => { - for (let key in shepherd.electrumServers) { - const _abbr = shepherd.electrumServers[key].abbr; - const { priv, pub } = shepherd.seedToWif(req.query.seed, shepherd.findNetworkObj(_abbr), req.query.iguana); - - shepherd.electrumKeys[_abbr] = { - priv, - pub, - }; - } - - shepherd.electrumCoins.auth = true; - - shepherd.log(JSON.stringify(shepherd.electrumKeys, null, '\t'), true); - - const successObj = { - msg: 'success', - result: 'true', - }; - - res.end(JSON.stringify(successObj)); -}); - -shepherd.get('/electrum/dev/logout', (req, res, next) => { - shepherd.electrumCoins.auth = false; - shepherd.electrumKeys = {}; - - const successObj = { - msg: 'success', - result: 'true', - }; - - res.end(JSON.stringify(successObj)); -}); - -// spv v2 -/*shepherd.get('/electrum/bip39/seed', (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: shepherd.electrumServers, - }, - }; - - res.end(JSON.stringify(successObj)); - - console.log(shepherd.bitcoinJS.networks.komodo); - const hdMaster = shepherd.bitcoinJS.HDNode.fromSeedBuffer(seed, shepherd.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 = shepherd.bitcoinJS.HDNode.fromSeedBuffer(seed, shepherd.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 = (txid, proof, pos) => { - const reverse = require('buffer-reverse'); - let hash = txid; - let serialized; - const _sha256 = (data) => { - return shepherd.crypto.createHash('sha256').update(data).digest(); - } - - shepherd.log(`getMerkleRoot txid ${txid}`, true); - shepherd.log(`getMerkleRoot pos ${pos}`, true); - shepherd.log('getMerkleRoot proof', true); - shepherd.log(`getMerkleRoot ${proof}`, true); - - 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 = (txid, height, serverList, mainServer) => { - // select random server - const getRandomIntInclusive = (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 shepherd.electrumJSCore(_mainServer[1], _mainServer[0], 'tcp'); // tcp or tls - - return new shepherd.Promise((resolve, reject) => { - shepherd.log(`main server: ${mainServer}`, true); - shepherd.log(`verification server: ${randomServer}`, true); - - ecl.connect(); - ecl.blockchainTransactionGetMerkle(txid, height) - .then((merkleData) => { - if (merkleData && - merkleData.merkle && - merkleData.pos) { - shepherd.log('electrum getmerkle =>', true); - shepherd.log(merkleData, true); - ecl.close(); - - const _res = shepherd.getMerkleRoot(txid, merkleData.merkle, merkleData.pos); - shepherd.log(_res, true); - - ecl = new shepherd.electrumJSCore(_randomServer[1], _randomServer[0], 'tcp'); - ecl.connect(); - - ecl.blockchainBlockGetHeader(height) - .then((blockInfo) => { - if (blockInfo && - blockInfo['merkle_root']) { - ecl.close(); - shepherd.log('blockinfo =>', true); - shepherd.log(blockInfo, true); - shepherd.log(blockInfo['merkle_root'], true); - - 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 = (coin, txid, height) => { - const _serverList = shepherd.electrumCoins[coin].serverList; - - shepherd.log(`verifyMerkleByCoin`, true); - shepherd.log(shepherd.electrumCoins[coin].server, true); - shepherd.log(shepherd.electrumCoins[coin].serverList, true); - - return new shepherd.Promise((resolve, reject) => { - if (_serverList !== 'none') { - let _filteredServerList = []; - - for (let i = 0; i < _serverList.length; i++) { - if (_serverList[i] !== shepherd.electrumCoins[coin].server.ip + ':' + shepherd.electrumCoins[coin].server.port) { - _filteredServerList.push(_serverList[i]); - } - } - - shepherd.verifyMerkle( - txid, - height, - _filteredServerList, - shepherd.electrumCoins[coin].server.ip + ':' + shepherd.electrumCoins[coin].server.port - ).then((proof) => { - resolve(proof); - }); - } else { - resolve(false); - } - }); -} - -shepherd.get('/electrum/merkle/verify', (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', (req, res, next) => { - if (req.query.abbr) { - let _electrumServers = {}; - - for (let key in shepherd.electrumServers) { - _electrumServers[shepherd.electrumServers[key].abbr] = shepherd.electrumServers[key]; - } - - const successObj = { - msg: 'success', - result: { - servers: _electrumServers, - }, - }; - - res.end(JSON.stringify(successObj)); - } else { - const successObj = { - msg: 'success', - result: { - servers: shepherd.electrumServers, - }, - }; - - res.end(JSON.stringify(successObj)); - } -}); - -shepherd.get('/electrum/coins/server/set', (req, res, next) => { - shepherd.electrumCoins[req.query.coin].server = { - ip: req.query.address, - port: req.query.port, - }; - - for (let key in shepherd.electrumServers) { - if (shepherd.electrumServers[key].abbr === req.query.coin) { // a bit risky - shepherd.electrumServers[key].address = req.query.address; - shepherd.electrumServers[key].port = req.query.port; - break; - } - } - - shepherd.log(JSON.stringify(shepherd.electrumCoins[req.query.coin], null, '\t'), true); - - const successObj = { - msg: 'success', - result: true, - }; - - res.end(JSON.stringify(successObj)); -}); - -shepherd.addElectrumCoin = (coin) => { - for (let key in shepherd.electrumServers) { - if (shepherd.electrumServers[key].abbr === coin) { - shepherd.electrumCoins[coin] = { - name: key, - abbr: coin, - server: { - ip: shepherd.electrumServers[key].address, - port: shepherd.electrumServers[key].port, - }, - serverList: shepherd.electrumServers[key].serverList ? shepherd.electrumServers[key].serverList : 'none', - txfee: 'calculated' /*shepherd.electrumServers[key].txfee*/, - }; - - return true; - } - } -} - -shepherd.get('/electrum/coins/remove', (req, res, next) => { - delete shepherd.electrumCoins[req.query.coin]; - - const successObj = { - msg: 'success', - result, - }; - - res.end(JSON.stringify(successObj)); -}); - -shepherd.get('/electrum/coins/add', (req, res, next) => { - const result = shepherd.addElectrumCoin(req.query.coin); - - const successObj = { - msg: 'success', - result, - }; - - res.end(JSON.stringify(successObj)); -}); - -shepherd.get('/electrum/coins', (req, res, next) => { - let _electrumCoins = JSON.parse(JSON.stringify(shepherd.electrumCoins)); // deep cloning - - for (let key in _electrumCoins) { - if (shepherd.electrumKeys[key]) { - _electrumCoins[key].pub = shepherd.electrumKeys[key].pub; - } - } - - const successObj = { - msg: 'success', - result: _electrumCoins, - }; - - res.end(JSON.stringify(successObj)); -}); - -shepherd.kmdCalcInterest = (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; - - shepherd.log('kmdCalcInterest', true); - shepherd.log(`locktime ${locktime}`, true); - shepherd.log(`minutes converted ${timestampDiffMinutes}`, true); - shepherd.log(`passed ${hoursPassed}h ${minutesPassed}m ${secondsPassed}s`, true); - - // calc interest - if (timestampDiffMinutes >= 60) { - if (timestampDiffMinutes > 365 * 24 * 60) { - timestampDiffMinutes = 365 * 24 * 60; - } - timestampDiffMinutes -= 59; - - shepherd.log(`minutes if statement ${timestampDiffMinutes}`, true); - - // 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; - shepherd.log(`interest ${interest}`, true); - } - - return interest; -} - -shepherd.get('/electrum/getbalance', (req, res, next) => { - const network = req.query.network || shepherd.findNetworkObj(req.query.coin); - const ecl = new shepherd.electrumJSCore(shepherd.electrumServers[network].port, shepherd.electrumServers[network].address, shepherd.electrumServers[network].proto); // tcp or tls - - shepherd.log('electrum getbalance =>', true); - - 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++) { - shepherd.log(`utxo ${utxoList[i]['tx_hash']} sats ${utxoList[i].value} value ${Number(utxoList[i].value) * 0.00000001}`, true); - - if (Number(utxoList[i].value) * 0.00000001 >= 10) { - _utxo.push(utxoList[i]); - } - } - - shepherd.log('filtered utxo list =>', true); - shepherd.log(_utxo, true); - - if (_utxo && - _utxo.length) { - let interestTotal = 0; - - shepherd.Promise.all(_utxo.map((_utxoItem, index) => { - return new shepherd.Promise((resolve, reject) => { - ecl.blockchainTransactionGet(_utxoItem['tx_hash']) - .then((_rawtxJSON) => { - shepherd.log('electrum gettransaction ==>', true); - shepherd.log((index + ' | ' + (_rawtxJSON.length - 1)), true); - shepherd.log(_rawtxJSON, true); - - // decode tx - const _network = shepherd.getNetworkData(network); - const decodedTx = shepherd.electrumJSTxDecoder(_rawtxJSON, _network); - - if (decodedTx && - decodedTx.format && - decodedTx.format.locktime > 0) { - interestTotal += shepherd.kmdCalcInterest(decodedTx.format.locktime, _utxoItem.value); - } - - shepherd.log('decoded tx =>', true); - shepherd.log(decodedTx, true); - - resolve(true); - }); - }); - })) - .then(promiseResult => { - ecl.close(); +// spv +shepherd = require('./shepherd/electrum/network.js')(shepherd); +shepherd = require('./shepherd/electrum/coins.js')(shepherd); +shepherd = require('./shepherd/electrum/keys.js')(shepherd); +shepherd = require('./shepherd/electrum/auth.js')(shepherd); +shepherd = require('./shepherd/electrum/merkle.js')(shepherd); +shepherd = require('./shepherd/electrum/balance.js')(shepherd); +shepherd = require('./shepherd/electrum/transactions.js')(shepherd); +shepherd = require('./shepherd/electrum/block.js')(shepherd); +shepherd = require('./shepherd/electrum/createtx.js')(shepherd); +shepherd = require('./shepherd/electrum/interest.js')(shepherd); +shepherd = require('./shepherd/electrum/listunspent.js')(shepherd); +shepherd = require('./shepherd/electrum/estimate.js')(shepherd); + +// dex +shepherd = require('./shepherd/dex/coind.js')(shepherd); + +shepherd = require('./shepherd/addCoinShortcuts.js')(shepherd); +shepherd = require('./shepherd/dashboardUpdate.js')(shepherd); +shepherd = require('./shepherd/binsTestUtil.js')(shepherd); +shepherd = require('./shepherd/binsUtils.js')(shepherd); +shepherd = require('./shepherd/downloadUtil.js')(shepherd); +shepherd = require('./shepherd/init.js')(shepherd); +shepherd = require('./shepherd/pin.js')(shepherd); +shepherd = require('./shepherd/downloadBins.js')(shepherd); +shepherd = require('./shepherd/downloadPatch.js')(shepherd); +shepherd = require('./shepherd/downloadZcparams.js')(shepherd); +shepherd = require('./shepherd/coinsList.js')(shepherd); +shepherd = require('./shepherd/quitDaemon.js')(shepherd); +shepherd = require('./shepherd/rpc.js')(shepherd); +shepherd = require('./shepherd/kickstart.js')(shepherd); +shepherd = require('./shepherd/debugLog.js')(shepherd); +shepherd = require('./shepherd/confMaxconnections.js')(shepherd); +shepherd = require('./shepherd/appInfo.js')(shepherd); +shepherd = require('./shepherd/daemonControl.js')(shepherd); +shepherd = require('./shepherd/auth.js')(shepherd); +shepherd = require('./shepherd/coins.js')(shepherd); + +shepherd.printDirs(); - 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(); - shepherd.log('electrum getbalance ==>', true); - shepherd.log(json, true); - - 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 = (transactions) => { - return transactions.sort((b, a) => { - if (a.height < b.height) { - return -1; - } - - if (a.height > b.height) { - return 1; - } - - return 0; - }); -} - -shepherd.get('/electrum/listtransactions', (req, res, next) => { - const network = req.query.network || shepherd.findNetworkObj(req.query.coin); - const ecl = new shepherd.electrumJSCore(shepherd.electrumServers[network].port, shepherd.electrumServers[network].address, shepherd.electrumServers[network].proto); // tcp or tls - - shepherd.log('electrum listtransactions ==>', true); - - if (!req.query.full) { - ecl.connect(); - ecl.blockchainAddressGetHistory(req.query.address) - .then((json) => { - ecl.close(); - shepherd.log(json, true); - - json = shepherd.sortTransactions(json); - - const successObj = { - msg: 'success', - result: 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); - let _rawtx = []; - - shepherd.log(json.length, true); - - shepherd.Promise.all(json.map((transaction, index) => { - return new shepherd.Promise((resolve, reject) => { - ecl.blockchainBlockGetHeader(transaction.height) - .then((blockInfo) => { - if (blockInfo && - blockInfo.timestamp) { - ecl.blockchainTransactionGet(transaction['tx_hash']) - .then((_rawtxJSON) => { - shepherd.log('electrum gettransaction ==>', true); - shepherd.log((index + ' | ' + (_rawtxJSON.length - 1)), true); - shepherd.log(_rawtxJSON, true); - - // decode tx - const _network = shepherd.getNetworkData(network); - const decodedTx = shepherd.electrumJSTxDecoder(_rawtxJSON, _network); - - let txInputs = []; - - shepherd.log('decodedtx =>', true); - shepherd.log(decodedTx.outputs, true); - - if (decodedTx && - decodedTx.inputs) { - shepherd.Promise.all(decodedTx.inputs.map((_decodedInput, index) => { - return new shepherd.Promise((_resolve, _reject) => { - if (_decodedInput.txid !== '0000000000000000000000000000000000000000000000000000000000000000') { - ecl.blockchainTransactionGet(_decodedInput.txid) - .then((rawInput) => { - const decodedVinVout = shepherd.electrumJSTxDecoder(rawInput, _network); - - shepherd.log('electrum raw input tx ==>', true); - - if (decodedVinVout) { - shepherd.log(decodedVinVout.outputs[_decodedInput.n], true); - 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) { - formattedTx.height = transaction.height; - formattedTx.blocktime = blockInfo.timestamp; - formattedTx.timereceived = blockInfo.timereceived; - formattedTx.hex = _rawtxJSON; - formattedTx.inputs = decodedTx.inputs; - formattedTx.outputs = decodedTx.outputs; - formattedTx.locktime = decodedTx.format.locktime; - _rawtx.push(formattedTx); - } else { - formattedTx[0].height = transaction.height; - formattedTx[0].blocktime = blockInfo.timestamp; - formattedTx[0].timereceived = blockInfo.timereceived; - formattedTx[0].hex = _rawtxJSON; - formattedTx[0].inputs = decodedTx.inputs; - formattedTx[0].outputs = decodedTx.outputs; - formattedTx[0].locktime = decodedTx.format.locktime; - formattedTx[1].height = transaction.height; - formattedTx[1].blocktime = blockInfo.timestamp; - formattedTx[1].timereceived = blockInfo.timereceived; - formattedTx[1].hex = _rawtxJSON; - formattedTx[1].inputs = decodedTx.inputs; - formattedTx[1].outputs = decodedTx.outputs; - formattedTx[1].locktime = decodedTx.format.locktime; - _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', (req, res, next) => { - const network = req.query.network || shepherd.findNetworkObj(req.query.coin); - const ecl = new shepherd.electrumJSCore(shepherd.electrumServers[network].port, shepherd.electrumServers[network].address, shepherd.electrumServers[network].proto); // tcp or tls - - shepherd.log('electrum gettransaction =>', true); - - ecl.connect(); - ecl.blockchainTransactionGet(req.query.txid) - .then((json) => { - ecl.close(); - shepherd.log(json, true); - - const successObj = { - msg: 'success', - result: json, - }; - - res.end(JSON.stringify(successObj)); - }); -}); - -shepherd.parseTransactionAddresses = (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, - }; - - shepherd.log('parseTransactionAddresses result ==>', true); - - 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++) { - shepherd.log(`key ==>`, true); - shepherd.log(_parse[key][i], true); - shepherd.log(Number(_parse[key][i].value), true); - - _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, - }; - } - - shepherd.log(_sum, true); - shepherd.log(result, true); - - return result; -} - -shepherd.get('/electrum/getblockinfo', (req, res, next) => { - shepherd.electrumGetBlockInfo(req.query.height, req.query.network) - .then((json) => { - const successObj = { - msg: 'success', - result: json, - }; - - res.end(JSON.stringify(successObj)); - }); -}); - -shepherd.electrumGetBlockInfo = (height, network) => { - return new shepherd.Promise((resolve, reject) => { - const ecl = new shepherd.electrumJSCore(shepherd.electrumServers[network].port, shepherd.electrumServers[network].address, shepherd.electrumServers[network].proto); // tcp or tls - - ecl.connect(); - ecl.blockchainBlockGetHeader(height) - .then((json) => { - ecl.close(); - shepherd.log('electrum getblockinfo ==>', true); - shepherd.log(json, true); - - resolve(json); - }); - }); -} - -shepherd.get('/electrum/getcurrentblock', (req, res, next) => { - shepherd.electrumGetCurrentBlock(req.query.network) - .then((json) => { - const successObj = { - msg: 'success', - result: json, - }; - - res.end(JSON.stringify(successObj)); - }); -}); - -shepherd.electrumGetCurrentBlock = (network) => { - return new shepherd.Promise((resolve, reject) => { - const ecl = new shepherd.electrumJSCore(shepherd.electrumServers[network].port, shepherd.electrumServers[network].address, shepherd.electrumServers[network].proto); // tcp or tls - - ecl.connect(); - ecl.blockchainNumblocksSubscribe() - .then((json) => { - ecl.close(); - shepherd.log('electrum currentblock ==>', true); - shepherd.log(json, true); - - resolve(json); - }); - }); -} - -shepherd.get('/electrum/decoderawtx', (req, res, next) => { - const _network = shepherd.getNetworkData(req.query.network); - const _rawtx = req.query.rawtx; - // const _rawtx = '0100000001dd6d064f5665f8454293ecaa9dbb55accf4f7e443d35f3b5ab7760f54b6c15fe000000006a473044022056355585a4a501ec9afc96aa5df124cf29ad3ac6454b47cd07cd7d89ec95ec2b022074c4604ee349d30e5336f210598e4dc576bf16ebeb67eeac3f4e82f56e930fee012103b90ba01af308757054e0484bb578765d5df59c4a57adbb94e2419df5e7232a63feffffff0289fc923b000000001976a91424af38fcb13bbc171b0b42bb017244a53b6bb2fa88ac20a10700000000001976a9142f4c0f91fc06ac228c120aee41741d0d3909683288ac49258b58'; - const decodedTx = shepherd.electrumJSTxDecoder(_rawtx, _network); - - shepherd.log('electrum decoderawtx input tx ==>', true); - - if (req.query.parseonly || - decodedTx.inputs[0].txid === '0000000000000000000000000000000000000000000000000000000000000000') { - const successObj = { - msg: 'success', - result: { - network: decodedTx.network, - format: decodedTx.format, - inputs: decodedTx.inputs, - outputs: decodedTx.outputs, - }, - }; - - shepherd.log(successObj.result, true); - - res.end(JSON.stringify(successObj)); - } else { - const ecl = new shepherd.electrumJSCore(shepherd.electrumServers[req.query.network].port, shepherd.electrumServers[req.query.network].address, shepherd.electrumServers[req.query.network].proto); // tcp or tls - - ecl.connect(); - ecl.blockchainTransactionGet(decodedTx.inputs[0].txid) - .then((json) => { - ecl.close(); - shepherd.log(json, true); - - const decodedVin = shepherd.electrumJSTxDecoder(json, _network); - - const successObj = { - msg: 'success', - result: { - 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 - }; -} - -// remove -shepherd.get('/electrum/subset', function(req, res, next) { - const _utxoSet = shepherd.findUtxoSet(null, Number(req.query.target) + Number(shepherd.electrumServers[req.query.network].txfee)); // target + txfee - - const successObj = { - msg: 'success', - result: { - utxoSet: _utxoSet.set, - change: _utxoSet.change, - }, - }; - - res.end(JSON.stringify(successObj)); +// default route +shepherd.get('/', (req, res, next) => { + res.send('Agama app server'); }); -// single sig -shepherd.buildSignedTx = (sendTo, changeAddress, wif, network, utxo, changeValue, spendValue) => { - let key = shepherd.bitcoinJS.ECPair.fromWIF(wif, shepherd.getNetworkData(network)); - let tx = new shepherd.bitcoinJS.TransactionBuilder(shepherd.getNetworkData(network)); - - shepherd.log('buildSignedTx'); - // console.log(`buildSignedTx priv key ${wif}`); - shepherd.log(`buildSignedTx pub key ${key.getAddress().toString()}`, true); - // console.log('buildSignedTx std tx fee ' + shepherd.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); - shepherd.log(`kmd tx locktime set to ${_locktime}`, true); - } - - shepherd.log('buildSignedTx unsigned tx data vin', true); - shepherd.log(tx.tx.ins, true); - shepherd.log('buildSignedTx unsigned tx data vout', true); - shepherd.log(tx.tx.outs, true); - shepherd.log('buildSignedTx unsigned tx data', true); - shepherd.log(tx, true); - - for (let i = 0; i < utxo.length; i++) { - tx.sign(i, key); - } - - const rawtx = tx.build().toHex(); - - shepherd.log('buildSignedTx signed tx hex', true); - shepherd.log(rawtx, true); - - return rawtx; -} - -shepherd.maxSpendBalance = (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', (req, res, next) => { - // txid 64 char - const network = req.query.network || shepherd.findNetworkObj(req.query.coin); - const ecl = new shepherd.electrumJSCore(shepherd.electrumServers[network].port, shepherd.electrumServers[network].address, shepherd.electrumServers[network].proto); // tcp or tls - const outputAddress = req.query.address; - const changeAddress = req.query.change; - const value = Number(req.query.value); - const push = req.query.push; - const fee = shepherd.electrumServers[network].txfee; - let wif = req.query.wif; - - if (req.query.gui) { - wif = shepherd.electrumKeys[req.query.coin].priv; - } - - shepherd.log('electrum createrawtx =>', true); - - 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, - }); - } - } - - shepherd.log('electrum listunspent unformatted ==>', true); - shepherd.log(utxoList, true); - - shepherd.log('electrum listunspent formatted ==>', true); - shepherd.log(utxoListFormatted, true); - - const _maxSpendBalance = Number(shepherd.maxSpendBalance(utxoListFormatted)); - let targets = [{ - address: outputAddress, - value: value > _maxSpendBalance ? _maxSpendBalance : value, - }]; - shepherd.log('targets =>', true); - shepherd.log(targets, true); - - 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 } = shepherd.coinSelect(utxoListFormatted, targets, feeRate); - - shepherd.log('coinselect res =>', true); - shepherd.log('coinselect inputs =>', true); - shepherd.log(inputs, true); - shepherd.log('coinselect outputs =>', true); - shepherd.log(outputs, true); - shepherd.log('coinselect calculated fee =>', true); - shepherd.log(fee, true); - - if (!inputs && - !outputs) { - targets[0].value = targets[0].value - shepherd.electrumServers[network].txfee; - shepherd.log('second run', true); - shepherd.log('coinselect adjusted targets =>', true); - shepherd.log(targets, true); - - const secondRun = shepherd.coinSelect(utxoListFormatted, targets, feeRate); - inputs = secondRun.inputs; - outputs = secondRun.outputs; - fee = secondRun.fee; - - shepherd.log('coinselect inputs =>', true); - shepherd.log(inputs, true); - shepherd.log('coinselect outputs =>', true); - shepherd.log(outputs, true); - shepherd.log('coinselect fee =>', true); - shepherd.log(fee, true); - } - - 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 { - shepherd.log(`maxspend ${_maxSpend} (${_maxSpend * 0.00000001})`, true); - shepherd.log(`value ${value}`, true); - shepherd.log(`sendto ${outputAddress} amount ${value} (${value * 0.00000001})`, true); - shepherd.log(`changeto ${changeAddress} amount ${_change} (${_change * 0.00000001})`, true); - - // account for KMD interest - if (network === 'komodo' && - totalInterest > 0) { - // account for extra vout - const _feeOverhead = outputs.length === 1 ? shepherd.estimateTxSize(0, 1) * feeRate : 0; - - shepherd.log(`max interest to claim ${totalInterest} (${totalInterest * 0.00000001})`, true); - shepherd.log(`estimated fee overhead ${_feeOverhead}`, true); - shepherd.log(`current change amount ${_change} (${_change * 0.00000001}), boosted change amount ${_change + (totalInterest - _feeOverhead)} (${(_change + (totalInterest - _feeOverhead)) * 0.00000001})`, true); - - _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; - } - - const _estimatedFee = vinSum - outputs[0].value - _change; - - shepherd.log(`vin sum ${vinSum} (${vinSum * 0.00000001})`, true); - shepherd.log(`estimatedFee ${_estimatedFee} (${_estimatedFee * 0.00000001})`, true); - - 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 shepherd.electrumJSCore(shepherd.electrumServers[network].port, shepherd.electrumServers[network].address, shepherd.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', (req, res, next) => { - const rawtx = req.query.rawtx; - const ecl = new shepherd.electrumJSCore(shepherd.electrumServers[req.query.network].port, shepherd.electrumServers[req.query.network].address, shepherd.electrumServers[req.query.network].proto); // tcp or tls - - ecl.connect(); - ecl.blockchainTransactionBroadcast(rawtx) - .then((json) => { - ecl.close(); - shepherd.log('electrum pushtx ==>', true); - shepherd.log(json, true); - - const successObj = { - msg: 'success', - result: json, - }; - - res.end(JSON.stringify(successObj)); - }); -}); - -shepherd.listunspent = (ecl, address, network, full, verify) => { - let _atLeastOneDecodeTxFailed = false; - - if (full) { - return new shepherd.Promise((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 { - shepherd.Promise.all(_utxo.map((_utxoItem, index) => { - return new shepherd.Promise((resolve, reject) => { - ecl.blockchainTransactionGet(_utxoItem['tx_hash']) - .then((_rawtxJSON) => { - shepherd.log('electrum gettransaction ==>', true); - shepherd.log(index + ' | ' + (_rawtxJSON.length - 1), true); - shepherd.log(_rawtxJSON, true); - - // decode tx - const _network = shepherd.getNetworkData(network); - const decodedTx = shepherd.electrumJSTxDecoder(_rawtxJSON, _network); - - shepherd.log('decoded tx =>', true); - shepherd.log(decodedTx, true); - - 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.kmdCalcInterest(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) { - shepherd.log(promiseResult, true); - resolve(promiseResult); - } else { - shepherd.log('listunspent error, cant decode tx(s)', true); - resolve('decode error'); - } - }); - } - } else { - resolve('cant get current height'); - } - }); - } else { - ecl.close(); - resolve(CONNECTION_ERROR_OR_INCOMPLETE_DATA); - } - }); - }); - } else { - return new shepherd.Promise((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', (req, res, next) => { - const network = req.query.network || shepherd.findNetworkObj(req.query.coin); - const ecl = new shepherd.electrumJSCore(shepherd.electrumServers[network].port, shepherd.electrumServers[network].address, shepherd.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) => { - shepherd.log('electrum listunspent ==>', true); - - const successObj = { - msg: 'success', - result: listunspent, - }; - - res.end(JSON.stringify(successObj)); - }); - } else { - shepherd.listunspent(ecl, req.query.address, network) - .then((listunspent) => { - ecl.close(); - shepherd.log('electrum listunspent ==>', true); - - const successObj = { - msg: 'success', - result: listunspent, - }; - - res.end(JSON.stringify(successObj)); - }); - } -}); - -shepherd.get('/electrum/estimatefee', (req, res, next) => { - const ecl = new shepherd.electrumJSCore(shepherd.electrumServers[req.query.network].port, shepherd.electrumServers[req.query.network].address, shepherd.electrumServers[req.query.network].proto); // tcp or tls - - ecl.connect(); - ecl.blockchainEstimatefee(req.query.blocks) - .then((json) => { - ecl.close(); - shepherd.log('electrum estimatefee ==>', true); - - const successObj = { - msg: 'success', - result: json, - }; - - res.end(JSON.stringify(successObj)); - }); -}); - -shepherd.estimateTxSize = (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', (req, res, next) => { - const successObj = { - msg: 'success', - result: shepherd.nativeCoindList, - }; - - res.end(JSON.stringify(successObj)); -}); - -shepherd.scanNativeCoindBins = () => { - let nativeCoindList = {}; - - // check if coind bins are present in agama - for (let key in shepherd.nativeCoind) { - nativeCoindList[key] = { - name: shepherd.nativeCoind[key].name, - port: shepherd.nativeCoind[key].port, - bin: shepherd.nativeCoind[key].bin, - bins: { - daemon: false, - cli: false, - }, - }; - - if (shepherd.fs.existsSync(`${shepherd.coindRootDir}/${key}/${shepherd.nativeCoind[key].bin}d${shepherd.os.platform() === 'win32' ? '.exe' : ''}`)) { - nativeCoindList[key].bins.daemon = true; - } - - if (shepherd.fs.existsSync(`${shepherd.coindRootDir}/${key}/${shepherd.nativeCoind[key].bin}-cli${shepherd.os.platform() === 'win32' ? '.exe' : ''}`)) { - nativeCoindList[key].bins.cli = true; - } - } - - return nativeCoindList; -} - -shepherd.getAppRuntimeLog = () => { - return new shepherd.Promise((resolve, reject) => { - resolve(appRuntimeLog); - }); -}; - -shepherd.startSPV = (coin) => { - if (coin === 'KMD+REVS+JUMBLR') { - shepherd.addElectrumCoin('KMD'); - shepherd.addElectrumCoin('REVS'); - shepherd.addElectrumCoin('JUMBLR'); - } else { - shepherd.addElectrumCoin(coin); - } -} - -shepherd.startKMDNative = (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, - }) - }; - - shepherd.request(options, (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], - }) - }; - - shepherd.request(options, (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', (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' - ]; - } - - const getAddressesNative = (coin) => { - const type = [ - 'public', - 'private' - ]; - - if (coin === 'CHIPS') { - type.pop(); - } - - shepherd.Promise.all(type.map((_type, index) => { - return new shepherd.Promise((resolve, reject) => { - _bitcoinRPC( - coin, - _type === 'public' ? 'getaddressesbyaccount' : 'z_listaddresses', - [''] - ).then((_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) { - const 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((elem, pos) => { - return result[0].indexOf(elem) === pos; - }); - } - if (result[1] && - result[1].length) { - result[1] = result[1].filter((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) { - shepherd.Promise.all(result[1].map((_address, index) => { - return new shepherd.Promise((resolve, reject) => { - _bitcoinRPC(coin, 'z_getbalance', [_address]) - .then((__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((__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)); - } - }) - } - - const _bitcoinRPC = (coin, cmd, params) => { - return new shepherd.Promise((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, - }; - - shepherd.request(options, (error, response, body) => { - if (response && - response.statusCode && - response.statusCode === 200) { - resolve(body); - } else { - resolve(body); - } - }); - }); - } - - shepherd.Promise.all(_promiseStack.map((_call, index) => { - let _params; - if (_call === 'listtransactions') { - _params = [ - '', - 300, - 0 - ]; - } - return new shepherd.Promise((resolve, reject) => { - _bitcoinRPC( - _coin, - _call, - _params - ) - .then((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 = () => { - return new shepherd.Promise((resolve, reject) => { - shepherd.fs.removeSync(`${iguanaTestDir}`); - resolve('done'); - }); -} - -shepherd.testBins = (daemonName) => { - return new shepherd.Promise((resolve, reject) => { - const _bins = { - komodod: shepherd.komododBin, - komodoCli: shepherd.komodocliBin, - }; - const _arg = null; - let _pid; - - shepherd.log('testBins exec ' + _bins[daemonName]); - - if (!shepherd.fs.existsSync(shepherd.agamaTestDir)) { - shepherd.fs.mkdirSync(shepherd.agamaTestDir); - } - - try { - shepherd._fs.access(`${shepherd.agamaTestDir}/${daemonName}Test.log`, shepherd.fs.constants.R_OK, (err) => { - if (!err) { - try { - shepherd._fs.unlinkSync(`${shepherd.agamaTestDir}/${daemonName}Test.log`); - } catch (e) {} - } else { - shepherd.log(`path ${shepherd.agamaTestDir}/${daemonName}Test.log doesnt exist`); - } - }); - } catch (e) {} - - if (daemonName === 'komodod') { - try { - shepherd._fs.access(`${iguanaTestDir}/debug.log`, shepherd.fs.constants.R_OK, (err) => { - if (!err) { - shepherd._fs.unlinkSync(`${iguanaTestDir}/db.log`); - shepherd._fs.unlinkSync(`${iguanaTestDir}/debug.log`); - shepherd._fs.unlinkSync(`${iguanaTestDir}/komodo.conf`); - shepherd._fs.unlinkSync(`${iguanaTestDir}/komodod.pid`); - shepherd._fs.unlinkSync(`${iguanaTestDir}/komodostate`); - shepherd._fs.unlinkSync(`${iguanaTestDir}/realtime`); - shepherd._fs.unlinkSync(`${iguanaTestDir}/wallet.dat`); - shepherd._fs.unlinkSync(`${iguanaTestDir}/.lock`); - shepherd.fs.removeSync(`${iguanaTestDir}/blocks`); - shepherd.fs.removeSync(`${iguanaTestDir}/chainstate`); - shepherd.fs.removeSync(`${iguanaTestDir}/database`); - execKomodod(); - } else { - shepherd.log(`test: nothing to remove in ${iguanaTestDir}`); - execKomodod(); - } - }); - } catch (e) {} - - const 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'; - - shepherd.fs.writeFile(`${iguanaTestDir}/komodo.conf`, _komodoConf, (err) => { - if (err) { - shepherd.log(`test: error writing komodo conf in ${iguanaTestDir}`); - } - }); - - shepherd.portscanner.checkPortStatus('7771', '127.0.0.1', (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: shepherd.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(() => { - const options = { - url: `http://localhost:7771`, - method: 'POST', - auth: { - user: 'user83f3afba8d714993', - pass: '0d4430ca1543833e35bce5a0cc9e16b3', - }, - body: JSON.stringify({ - agent: 'bitcoinrpc', - method: 'getinfo', - }), - }; - - shepherd.request(options, (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(() => { - 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) - - /*shepherd.execFile(`${shepherd.komododBin}`, _arg, { - 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 = (path) => { - return new shepherd.Promise((resolve, reject) => { - if (shepherd.path.indexOf(' ') > -1) { - shepherd.log(`error testing path ${path}`); - resolve(-1); - } else { - shepherd.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 = () => { - const osPlatform = shepherd.os.platform(); - const _bins = [ - shepherd.komododBin, - shepherd.komodocliBin - ]; - - if (osPlatform === 'darwin' || - osPlatform === 'linux') { - for (let i = 0; i < _bins.length; i++) { - shepherd._fs.stat(_bins[i], (err, stat) => { - if (!err) { - if (parseInt(stat.mode.toString(8), 10) !== 100775) { - shepherd.log(`${_bins[i]} fix permissions`); - shepherd.fsnode.chmodSync(_bins[i], '0775'); - } - } else { - shepherd.log(`error: ${_bins[i]} not found`); - } - }); - } - } -} - -shepherd.killRogueProcess = (processName) => { - // kill rogue process copies on start - let processGrep; - const osPlatform = shepherd.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; - } - - shepherd.exec(processGrep, (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)`); - - shepherd.exec(pkillCmd, (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 = () => { - let _checkList = { - rootDir: shepherd._fs.existsSync(shepherd.zcashParamsDir), - provingKey: shepherd._fs.existsSync(`${shepherd.zcashParamsDir}/sprout-proving.key`), - provingKeySize: false, - verifyingKey: shepherd._fs.existsSync(`${shepherd.zcashParamsDir}/sprout-verifying.key`), - verifyingKeySize: false, - errors: false, - }; - - if (_checkList.rootDir && - _checkList.provingKey || - _checkList.verifyingKey) { - // verify each key size - const _provingKeySize = _checkList.provingKey ? shepherd.fs.lstatSync(`${shepherd.zcashParamsDir}/sprout-proving.key`) : 0; - const _verifyingKeySize = _checkList.verifyingKey ? shepherd.fs.lstatSync(`${shepherd.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 = () => { - // read app version - const rootLocation = shepherd.path.join(__dirname, '../'); - const localVersionFile = shepherd.fs.readFileSync(`${rootLocation}version`, 'utf8'); - - return localVersionFile; -} - -shepherd.createAgamaDirs = () => { - if (!shepherd.fs.existsSync(shepherd.agamaDir)) { - shepherd.fs.mkdirSync(shepherd.agamaDir); - - if (shepherd.fs.existsSync(shepherd.agamaDir)) { - shepherd.log(`created agama folder at ${shepherd.agamaDir}`); - shepherd.writeLog(`created agama folder at ${shepherd.agamaDir}`); - } - } else { - shepherd.log('agama folder already exists'); - } - - if (!shepherd.fs.existsSync(`${shepherd.agamaDir}/shepherd`)) { - shepherd.fs.mkdirSync(`${shepherd.agamaDir}/shepherd`); - - if (shepherd.fs.existsSync(`${shepherd.agamaDir}/shepherd`)) { - shepherd.log(`created shepherd folder at ${shepherd.agamaDir}/shepherd`); - shepherd.writeLog(`create shepherd folder at ${shepherd.agamaDir}/shepherd`); - } - } else { - shepherd.log('agama/shepherd folder already exists'); - } - - if (!shepherd.fs.existsSync(`${shepherd.agamaDir}/shepherd/pin`)) { - shepherd.fs.mkdirSync(`${shepherd.agamaDir}/shepherd/pin`); - - if (shepherd.fs.existsSync(`${shepherd.agamaDir}/shepherd/pin`)) { - shepherd.log(`created pin folder at ${shepherd.agamaDir}/shepherd/pin`); - shepherd.writeLog(`create pin folder at ${shepherd.agamaDir}/shepherd/pin`); - } - } else { - shepherd.log('shepherd/pin folder already exists'); - } -} - -/* - * type: POST - * params: none - */ -shepherd.post('/encryptkey', (req, res, next) => { - if (req.body.key && - req.body.string && - req.body.pubkey) { - const encryptedString = shepherd.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}$'); - - shepherd.fs.writeFile(`${shepherd.agamaDir}/shepherd/pin/${req.body.pubkey}.pin`, encryptedString, (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', (req, res, next) => { - if (req.body.key && - req.body.pubkey) { - if (shepherd.fs.existsSync(`${shepherd.agamaDir}/shepherd/pin/${req.body.pubkey}.pin`)) { - shepherd.fs.readFile(`${shepherd.agamaDir}/shepherd/pin/${req.body.pubkey}.pin`, 'utf8', (err, data) => { - if (err) { - const errorObj = { - msg: 'error', - result: err, - }; - - res.end(JSON.stringify(errorObj)); - } else { - const encryptedKey = shepherd.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', (req, res, next) => { - if (shepherd.fs.existsSync(`${shepherd.agamaDir}/shepherd/pin`)) { - shepherd.fs.readdir(`${shepherd.agamaDir}/shepherd/pin`, (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 - */ -const downloadFile = (configuration) => { - return new shepherd.Promise((resolve, reject) => { - // Save variable to know progress - let receivedBytes = 0; - let totalBytes = 0; - - let req = shepherd.request({ - method: 'GET', - uri: configuration.remoteFile, - agentOptions: { - keepAlive: true, - keepAliveMsecs: 15000, - }, - }); - - let out = shepherd.fs.createWriteStream(configuration.localFile); - req.pipe(out); - - req.on('response', (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', (chunk) => { - // Update the received bytes - receivedBytes += chunk.length; - configuration.onProgress(receivedBytes, totalBytes); - }); - } else { - req.on('data', (chunk) => { - // Update the received bytes - receivedBytes += chunk.length; - }); - } - - req.on('end', () => { - 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', (req, res, next) => { - const rootLocation = shepherd.path.join(__dirname, '../'); - const successObj = { - msg: 'success', - result: 'bins', - }; - - res.end(JSON.stringify(successObj)); - - const _os = shepherd.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++) { - shepherd.remoteFileSize(remoteBinLocation[_os] + latestBins[_os][i], (err, remoteBinSize) => { - const localBinSize = shepherd.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', (req, res, next) => { - const rootLocation = shepherd.path.join(__dirname, '../'); - const _os = shepherd.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: (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(() => { - // verify that remote file is matching to DL'ed file - const localBinSize = shepherd.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', (req, res, next) => { - const successObj = { - msg: 'success', - result: 'dl started' - }; - - res.end(JSON.stringify(successObj)); - - shepherd.updateAgama(); -}); - -shepherd.updateAgama = () => { - const rootLocation = shepherd.path.join(__dirname, '../'); - - downloadFile({ - remoteFile: 'https://github.com/pbca26/dl-test/raw/master/patch.zip', - localFile: `${rootLocation}patch.zip`, - onProgress: (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(() => { - shepherd.remoteFileSize('https://github.com/pbca26/dl-test/raw/master/patch.zip', (err, remotePatchSize) => { - // verify that remote file is matching to DL'ed file - const localPatchSize = shepherd.fs.statSync(`${rootLocation}patch.zip`).size; - shepherd.log('compare dl file size'); - - if (localPatchSize === remotePatchSize) { - const zip = new shepherd.AdmZip(`${rootLocation}patch.zip`); - - shepherd.log('patch succesfully downloaded'); - shepherd.log('extracting contents'); - - if (shepherd.appConfig.dev) { - if (!shepherd.fs.existsSync(`${rootLocation}/patch`)) { - shepherd.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', - }, - }); - shepherd.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', (req, res, next) => { - const rootLocation = shepherd.path.join(__dirname, '../'); - const options = { - url: 'https://github.com/pbca26/dl-test/raw/master/version', - method: 'GET', - }; - - shepherd.request(options, (error, response, body) => { - if (response && - response.statusCode && - response.statusCode === 200) { - const remoteVersion = body.split('\n'); - const localVersionFile = shepherd.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', (req, res, next) => { - const dlLocation = shepherd.path.join(__dirname, '../'); - const zip = new shepherd.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', (req, res, next) => { - // const dlLocation = shepherd.zcashParamsDir + '/test'; - const dlLocation = shepherd.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: (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(() => { - 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', (req, res, next) => { - if (shepherd.fs.existsSync(`${shepherd.agamaDir}/shepherd/coinslist.json`)) { - shepherd.fs.readFile(`${shepherd.agamaDir}/shepherd/coinslist.json`, 'utf8', (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', (req, res, next) => { - const logLocation = `${shepherd.agamaDir}/shepherd`; - - if (!shepherd.guiLog[shepherd.appSessionHash]) { - shepherd.guiLog[shepherd.appSessionHash] = {}; - } - - if (shepherd.guiLog[shepherd.appSessionHash][req.body.timestamp]) { - shepherd.guiLog[shepherd.appSessionHash][req.body.timestamp].status = req.body.status; - shepherd.guiLog[shepherd.appSessionHash][req.body.timestamp].response = req.body.response; - } else { - shepherd.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, - }; - } - - shepherd.fs.writeFile(`${logLocation}/agamalog.json`, JSON.stringify(shepherd.guiLog), (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', (req, res, next) => { - const logExt = req.query.type === 'txt' ? 'txt' : 'json'; - - if (shepherd.fs.existsSync(`${shepherd.agamaDir}/shepherd/agamalog.${logExt}`)) { - shepherd.fs.readFile(`${shepherd.agamaDir}/shepherd/agamalog.${logExt}`, 'utf8', (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', (req, res, next) => { - const _payload = req.body.payload; - - if (!_payload) { - const errorObj = { - msg: 'error', - result: 'no payload provided', - }; - - res.end(JSON.stringify(errorObj)); - } else { - shepherd.fs.writeFile(`${shepherd.agamaDir}/shepherd/coinslist.json`, JSON.stringify(_payload), (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 = (timeout = 100) => { - // if komodod is under heavy load it may not respond to cli stop the first time - // exit komodod gracefully - let coindExitInterval = {}; - shepherd.lockDownAddCoin = true; - - for (let key in shepherd.coindInstanceRegistry) { - const chain = key !== 'komodod' ? key : null; - let _coindQuitCmd = shepherd.komodocliBin; - - // any coind - if (shepherd.nativeCoindList[key.toLowerCase()]) { - _coindQuitCmd = `${shepherd.coindRootDir}/${key.toLowerCase()}/${shepherd.nativeCoindList[key.toLowerCase()].bin.toLowerCase()}-cli`; - } - if (key === 'CHIPS') { - _coindQuitCmd = shepherd.chipscliBin; - } - - const execCliStop = () => { - let _arg = []; - if (chain && !shepherd.nativeCoindList[key.toLowerCase()] && key !== 'CHIPS') { - _arg.push(`-ac_name=${chain}`); - - if (shepherd.appConfig.dataDir.length) { - _arg.push(`-datadir=${shepherd.appConfig.dataDir + (key !== 'komodod' ? '/' + key : '')}`); - } - } else if (key === 'komodod' && shepherd.appConfig.dataDir.length) { - _arg.push(`-datadir=${shepherd.appConfig.dataDir}`); - } - - _arg.push('stop'); - shepherd.execFile(`${_coindQuitCmd}`, _arg, (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 shepherd.coindInstanceRegistry[key]; - clearInterval(coindExitInterval[key]); - } - - // workaround for AGT-65 - const _port = shepherd.assetChainPorts[key]; - setTimeout(() => { - shepherd.portscanner.checkPortStatus(_port, '127.0.0.1', (error, status) => { - // Status is 'open' if currently in use or 'closed' if available - if (status === 'closed') { - delete shepherd.coindInstanceRegistry[key]; - clearInterval(coindExitInterval[key]); - } - }); - }, 100); - - if (error !== null) { - shepherd.log(`exec error: ${error}`); - } - - if (key === 'CHIPS') { - setTimeout(() => { - shepherd.killRogueProcess('chips-cli'); - }, 100); - } else { - setTimeout(() => { - shepherd.killRogueProcess('komodo-cli'); - }, 100); - } - }); - } - - execCliStop(); - coindExitInterval[key] = setInterval(() => { - execCliStop(); - }, timeout); - } -} - -shepherd.getConf = (chain) => { - let _confLocation = chain === 'komodod' ? `${shepherd.komodoDir}/komodo.conf` : `${shepherd.komodoDir}/${chain}/${chain}.conf`; - _confLocation = chain === 'CHIPS' ? `${shepherd.chipsDir}/chips.conf` : _confLocation; - - // any coind - if (chain) { - if (shepherd.nativeCoindList[chain.toLowerCase()]) { - const _osHome = shepherd.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 (shepherd.fs.existsSync(_confLocation)) { - let _port = shepherd.assetChainPorts[chain]; - const _rpcConf = shepherd.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()]) { - shepherd.rpcConf[chain] = parsedRpcConfig; - } else { - shepherd.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', (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 (!shepherd.rpcConf[_chain]) { - shepherd.getConf(req.body.payload.chain === 'KMD' || !req.body.payload.chain && shepherd.kmdMainPassiveMode ? 'komodod' : req.body.payload.chain); - } - - if (_mode === 'default') { - /*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 - shepherd.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 = shepherd.os.platform === 'win32' ? process.env.APPDATA : process.env.HOME; - let coindDebugLogLocation; - - if (_chain === 'CHIPS') { - coindDebugLogLocation = `${shepherd.chipsDir}/debug.log`; - } else { - coindDebugLogLocation = `${_osHome}/.${shepherd.nativeCoindList[_chain.toLowerCase()].bin.toLowerCase()}/debug.log`; - } - - shepherd.readDebugLog(coindDebugLogLocation, 1) - .then((result) => { - const _obj = { - 'msg': 'success', - 'result': result, - }; - - // shepherd.log('bitcoinrpc debug ====>'); - // console.log(result); - - res.end(JSON.stringify(_obj)); - }, (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, - }; - } - - if (req.body.payload.chain) { - const options = { - url: `http://localhost:${shepherd.rpcConf[req.body.payload.chain].port}`, - method: 'POST', - auth: { - 'user': shepherd.rpcConf[req.body.payload.chain].user, - 'pass': shepherd.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 - shepherd.request(options, (error, response, body) => { - if (response && - response.statusCode && - response.statusCode === 200) { - res.end(body); - } else { - res.end(body); - } - }); - } - } - } else { - let _coindCliBin = shepherd.komodocliBin; - - if (shepherd.nativeCoindList && - _chain && - shepherd.nativeCoindList[_chain.toLowerCase()]) { - _coindCliBin = `${shepherd.coindRootDir}/${_chain.toLowerCase()}/${shepherd.nativeCoindList[_chain.toLowerCase()].bin.toLowerCase()}-cli`; - } - - let _arg = (_chain ? ' -ac_name=' + _chain : '') + ' ' + _cmd + _params; - - if (shepherd.appConfig.dataDir.length) { - _arg = `${_arg} -datadir=${shepherd.appConfig.dataDir + (_chain ? '/' + key : '')}`; - } - - _arg = _arg.trim().split(' '); - shepherd.execFile(_coindCliBin, _arg, (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', (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', (req, res, next) => { - shepherd.saveLocalAppConf(shepherd.defaultAppConfig); - - const successObj = { - msg: 'success', - result: 'config saved', - }; - - res.end(JSON.stringify(successObj)); -}); - -shepherd.log(`agama dir: ${shepherd.agamaDir}`); -shepherd.log('--------------------------') -shepherd.log(`komodo dir: ${shepherd.komododBin}`); -shepherd.log(`komodo bin: ${shepherd.komodoDir}`); -shepherd.writeLog(`agama dir: ${shepherd.agamaDir}`); -shepherd.writeLog(`komodo dir: ${shepherd.komododBin}`); -shepherd.writeLog(`komodo bin: ${shepherd.komodoDir}`); - -// default route -shepherd.get('/', (req, res, next) => { - res.send('Agama app server'); -}); - -/* - * type: GET - * - */ -shepherd.get('/appconf', (req, res, next) => { - const obj = shepherd.loadLocalConfig(); - res.send(obj); -}); - -/* - * type: GET - * - */ -shepherd.get('/sysinfo', (req, res, next) => { - const obj = shepherd.SystemInfo(); - res.send(obj); -}); - -/* - * type: GET - * - */ -shepherd.get('/appinfo', (req, res, next) => { - const obj = shepherd.appInfo(); - res.send(obj); -}); - -// expose sockets obj -shepherd.setIO = (io) => { - shepherd.io = io; -}; +// expose sockets obj +shepherd.setIO = (io) => { + shepherd.io = io; +}; shepherd.setVar = (_name, _body) => { shepherd[_name] = _body; }; -/* - * type: GET - * - */ -shepherd.get('/InstantDEX/allcoins', (req, res, next) => { - let successObj; - let nativeCoindList = []; - let electrumCoinsList = []; - - for (let key in shepherd.electrumCoins) { - if (key !== 'auth') { - electrumCoinsList.push(shepherd.electrumCoins[key].abbr); - } - } - - for (let key in shepherd.coindInstanceRegistry) { - nativeCoindList.push(key === 'komodod' ? 'KMD' : key); - } - - successObj = { - native: nativeCoindList, - spv: electrumCoinsList, - total: Object.keys(shepherd.electrumCoins).length - 1 + Object.keys(nativeCoindList).length, - }; - - res.end(JSON.stringify(successObj)); -}); - -/* - * type: GET - * - */ -shepherd.get('/auth/status', (req, res, next) => { // not finished - let successObj; - let _status = false; - - if (Object.keys(shepherd.coindInstanceRegistry).length) { - if (Object.keys(shepherd.electrumCoins).length > 1 && shepherd.electrumCoins.auth) { - _status = true; - } else if (Object.keys(shepherd.electrumCoins).length === 1 && !shepherd.electrumCoins.auth) { - _status = true; - } - } else if (Object.keys(shepherd.electrumCoins).length > 1 && shepherd.electrumCoins.auth) { - _status = true; - } else if (Object.keys(shepherd.electrumCoins).length === 1 && !Object.keys(shepherd.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', (req, res) => { - let _herd = req.body.herdname; - let _ac = req.body.ac; - let _lastNLines = req.body.lastLines; - let _location; - - if (shepherd.os.platform() === 'darwin') { - shepherd.komodoDir = shepherd.appConfig.dataDir.length ? shepherd.appConfig.dataDir : `${process.env.HOME}/Library/Application Support/Komodo`; - } - - if (shepherd.os.platform() === 'linux') { - shepherd.komodoDir = shepherd.appConfig.dataDir.length ? shepherd.appConfig.dataDir : `${process.env.HOME}/.komodo`; - } - - if (shepherd.os.platform() === 'win32') { - shepherd.komodoDir = shepherd.appConfig.dataDir.length ? shepherd.appConfig.dataDir : `${process.env.APPDATA}/Komodo`; - shepherd.komodoDir = shepherd.path.normalize(shepherd.komodoDir); - } - - if (_herd === 'komodo') { - _location = shepherd.komodoDir; - } - - if (_ac) { - _location = `${shepherd.komodoDir}/${_ac}`; - - if (_ac === 'CHIPS') { - _location = shepherd.chipsDir; - } - } - - shepherd.readDebugLog(`${_location}/debug.log`, _lastNLines) - .then((result) => { - const _obj = { - msg: 'success', - result: result, - }; - - res.end(JSON.stringify(_obj)); - }, (result) => { - const _obj = { - msg: 'error', - result: result, - }; - - res.end(JSON.stringify(_obj)); - }); -}); - -/* - * type: POST - * params: herd - */ -shepherd.post('/herd', (req, res) => { - shepherd.log('======= req.body ======='); - shepherd.log(req.body); - - if (req.body.options && - !shepherd.kmdMainPassiveMode) { - const testCoindPort = (skipError) => { - if (!shepherd.lockDownAddCoin) { - const _port = shepherd.assetChainPorts[req.body.options.ac_name]; - - shepherd.portscanner.checkPortStatus(_port, '127.0.0.1', (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(() => { - 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', (req, res) => { - shepherd.log('======= req.body ======='); - shepherd.log(req.body); - - if (shepherd.os.platform() === 'win32' && - req.body.chain == 'komodod') { - setkomodoconf = spawn(shepherd.path.join(__dirname, '../assets/bin/win64/genkmdconf.bat')); - } else { - shepherd.setConf(req.body.chain); - } - - const obj = { - msg: 'success', - result: 'result', - }; - - res.end(JSON.stringify(obj)); -}); - -/* - * type: POST - */ -shepherd.post('/getconf', (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', (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 = shepherd.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 = (fileLocation, lastNLines) => { - return new shepherd.Promise( - (resolve, reject) => { - if (lastNLines) { - try { - shepherd._fs.access(fileLocation, shepherd.fs.constants.R_OK, (err) => { - if (err) { - shepherd.log(`error reading ${fileLocation}`); - shepherd.writeLog(`error reading ${fileLocation}`); - reject(`readDebugLog error: ${err}`); - } else { - shepherd.log(`reading ${fileLocation}`); - shepherd._fs.readFile(fileLocation, 'utf-8', (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!'); - } - } - ); -}; - -// TODO: json.stringify wrapper - -const herder = (flock, data, coind) => { - if (data === undefined) { - data = 'none'; - shepherd.log('it is undefined'); - } - - shepherd.log(`herder flock: ${flock} coind: ${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' ? `${shepherd.komodoDir}/${data.ac_name}` : shepherd.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}`); - - // datadir case, check if komodo/chain folder exists - if (shepherd.appConfig.dataDir.length && - data.ac_name !== 'komodod') { - const _dir = data.ac_name !== 'komodod' ? `${shepherd.komodoDir}/${data.ac_name}` : shepherd.komodoDir; - - try { - shepherd._fs.accessSync(_dir, shepherd.fs.R_OK | shepherd.fs.W_OK); - - shepherd.log(`komodod datadir ${_dir} exists`); - } catch (e) { - shepherd.log(`komodod datadir ${_dir} access err: ${e}`); - shepherd.log(`attempting to create komodod datadir ${_dir}`); - - shepherd.fs.mkdirSync(_dir); - - if (shepherd.fs.existsSync(_dir)) { - shepherd.log(`created komodod datadir folder at ${_dir}`); - } else { - shepherd.log(`unable to create komodod datadir folder at ${_dir}`); - } - } - } - - // truncate debug.log - if (!shepherd.kmdMainPassiveMode) { - try { - const _confFileAccess = shepherd._fs.accessSync(kmdDebugLogLocation, shepherd.fs.R_OK | shepherd.fs.W_OK); - - if (_confFileAccess) { - shepherd.log(`error accessing ${kmdDebugLogLocation}`); - shepherd.writeLog(`error accessing ${kmdDebugLogLocation}`); - } else { - try { - shepherd.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 = shepherd.assetChainPorts[data.ac_name]; - - try { - // check if komodod instance is already running - shepherd.portscanner.checkPortStatus(_port, '127.0.0.1', (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 !== 'komodod' ? '/' + data.ac_name : ''); - } - - shepherd.log(`exec ${shepherd.komododBin} ${data.ac_options.join(' ')}${_customParam}`); - shepherd.writeLog(`exec ${shepherd.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}`); - - shepherd.coindInstanceRegistry[data.ac_name] = true; - if (!shepherd.kmdMainPassiveMode) { - let _arg = `${coindACParam}${data.ac_options.join(' ')}${_customParam}`; - _arg = _arg.trim().split(' '); - shepherd.execFile(`${shepherd.komododBin}`, _arg, { - maxBuffer: 1024 * 1000000 // 1000 mb - }, (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) { - shepherd.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}`); - } - } - - // TODO: refactor - if (flock === 'chipsd') { - let kmdDebugLogLocation = `${shepherd.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 = shepherd._fs.accessSync(kmdDebugLogLocation, shepherd.fs.R_OK | shepherd.fs.W_OK); - - if (_confFileAccess) { - shepherd.log(`error accessing ${kmdDebugLogLocation}`); - shepherd.writeLog(`error accessing ${kmdDebugLogLocation}`); - } else { - try { - shepherd.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 = shepherd.assetChainPorts.chipsd; - - try { - // check if komodod instance is already running - shepherd.portscanner.checkPortStatus(_port, '127.0.0.1', (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 ${shepherd.chipsBin} ${_customParam}`); - shepherd.writeLog(`exec ${shepherd.chipsBin} ${_customParam}`); - - shepherd.log(`daemon param ${data.ac_custom_param}`); - - shepherd.coindInstanceRegistry['CHIPS'] = true; - let _arg = `${_customParam}`; - _arg = _arg.trim().split(' '); - - if (_arg && - _arg.length > 1) { - shepherd.execFile(`${shepherd.chipsBin}`, _arg, { - maxBuffer: 1024 * 1000000 // 1000 mb - }, (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 { - shepherd.execFile(`${shepherd.chipsBin}`, { - maxBuffer: 1024 * 1000000 // 1000 mb - }, (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 = `${shepherd.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: shepherd.zcashdBin, // path to binary - name: data.ac_name, // REVS, USD, EUR etc. - exec_mode: 'fork', - cwd: shepherd.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 = shepherd.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 { - shepherd._fs.access(coindDebugLogLocation, shepherd.fs.constants.R_OK, (err) => { - if (err) { - shepherd.log(`error accessing ${coindDebugLogLocation}`); - shepherd.writeLog(`error accessing ${coindDebugLogLocation}`); - } else { - shepherd.log(`truncate ${coindDebugLogLocation}`); - shepherd.writeLog(`truncate ${coindDebugLogLocation}`); - shepherd.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 = `${shepherd.coindRootDir}/${coind.toLowerCase()}/${shepherd.nativeCoindList[coind.toLowerCase()].bin.toLowerCase()}d`; - - try { - // check if coind instance is already running - shepherd.portscanner.checkPortStatus(_port, '127.0.0.1', (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(' ')}`); - - shepherd.coindInstanceRegistry[coind] = true; - let _arg = `${data.ac_options.join(' ')}`; - _arg = _arg.trim().split(' '); - shepherd.execFile(`${coindBin}`, _arg, { - maxBuffer: 1024 * 1000000 // 1000 mb - }, (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 = (isChips) => { - // check if kmd conf exists - shepherd._fs.access(isChips ? `${shepherd.chipsDir}/chips.conf` : `${shepherd.komodoDir}/komodo.conf`, shepherd.fs.constants.R_OK, (err) => { - if (err) { - shepherd.log(isChips ? 'creating chips conf' : 'creating komodo conf'); - shepherd.writeLog(isChips ? `creating chips conf in ${shepherd.chipsDir}/chips.conf` : `creating komodo conf in ${shepherd.komodoDir}/komodo.conf`); - shepherd.setConf(isChips ? 'chipsd' : 'komodod'); - } else { - const _confSize = shepherd.fs.lstatSync(isChips ? `${shepherd.chipsDir}/chips.conf` : `${shepherd.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 ${shepherd.chipsDir}/chips.conf` : `creating komodo conf in ${shepherd.komodoDir}/komodo.conf`); - shepherd.setConf(isChips ? 'chipsd' : 'komodod'); - } else { - shepherd.writeLog(isChips ? 'chips conf exists' : 'komodo conf exists'); - shepherd.log(isChips ? 'chips conf exists' : 'komodo conf exists'); - } - } - }); -} - -const setConf = (flock, coind) => { - let nativeCoindDir; - let DaemonConfPath; - - shepherd.log(flock); - shepherd.writeLog(`setconf ${flock}`); - - if (shepherd.os.platform() === 'darwin') { - nativeCoindDir = coind ? `${process.env.HOME}/Library/Application Support/${shepherd.nativeCoindList[coind.toLowerCase()].bin}` : null; - } - - if (shepherd.os.platform() === 'linux') { - nativeCoindDir = coind ? `${process.env.HOME}/.${shepherd.nativeCoindList[coind.toLowerCase()].bin.toLowerCase()}` : null; - } - - if (shepherd.os.platform() === 'win32') { - nativeCoindDir = coind ? `${process.env.APPDATA}/${shepherd.nativeCoindList[coind.toLowerCase()].bin}` : null; - } - - switch (flock) { - case 'komodod': - DaemonConfPath = `${shepherd.komodoDir}/komodo.conf`; - - if (shepherd.os.platform() === 'win32') { - DaemonConfPath = shepherd.path.normalize(DaemonConfPath); - } - break; - case 'zcashd': - DaemonConfPath = `${shepherd.ZcashDir}/zcash.conf`; - - if (shepherd.os.platform() === 'win32') { - DaemonConfPath = shepherd.path.normalize(DaemonConfPath); - } - break; - case 'chipsd': - DaemonConfPath = `${shepherd.chipsDir}/chips.conf`; - - if (shepherd.os.platform() === 'win32') { - DaemonConfPath = shepherd.path.normalize(DaemonConfPath); - } - break; - case 'coind': - DaemonConfPath = `${nativeCoindDir}/${shepherd.nativeCoindList[coind.toLowerCase()].bin.toLowerCase()}.conf`; - - if (shepherd.os.platform() === 'win32') { - DaemonConfPath = shepherd.path.normalize(DaemonConfPath); - } - break; - default: - DaemonConfPath = `${shepherd.komodoDir}/${flock}/${flock}.conf`; - - if (shepherd.os.platform() === 'win32') { - DaemonConfPath = shepherd.path.normalize(DaemonConfPath); - } - } - - shepherd.log(DaemonConfPath); - shepherd.writeLog(`setconf ${DaemonConfPath}`); - - const CheckFileExists = () => { - return new shepherd.Promise((resolve, reject) => { - const result = 'Check Conf file exists is done'; - - const confFileExist = shepherd.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 = () => { - return new shepherd.Promise((resolve, reject) => { - const result = 'Conf file permissions updated to Read/Write'; - - shepherd.fsnode.chmodSync(DaemonConfPath, '0666'); - shepherd.log(result); - shepherd.writeLog(`setconf ${result}`); - - resolve(result); - }); - } - - const RemoveLines = () => { - return new shepherd.Promise((resolve, reject) => { - const result = 'RemoveLines is done'; - - shepherd.fs.readFile(DaemonConfPath, 'utf8', (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'); - - shepherd.fs.writeFile(DaemonConfPath, rmlines, 'utf8', (err) => { - if (err) - return shepherd.log(err); - - shepherd.fsnode.chmodSync(DaemonConfPath, '0666'); - shepherd.writeLog(`setconf ${result}`); - shepherd.log(result); - resolve(result); - }); - }); - }); - } - - const CheckConf = () => { - return new shepherd.Promise((resolve, reject) => { - const result = 'CheckConf is done'; - - shepherd.setconf.status(DaemonConfPath, (err, status) => { - const rpcuser = () => { - return new shepherd.Promise((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'); - - shepherd.fs.appendFile(DaemonConfPath, `\nrpcuser=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 = () => { - return new shepherd.Promise((resolve, reject) => { - const result = 'checking rpcpassword...'; - - if (status[0].hasOwnProperty('rpcpassword')) { - shepherd.log('rpcpassword: OK'); - shepherd.writeLog('rpcpassword: OK'); - } else { - const randomstring = md5((Math.random() * Math.random() * 999).toString()); - - shepherd.log('rpcpassword: NOT FOUND'); - shepherd.writeLog('rpcpassword: NOT FOUND'); - - shepherd.fs.appendFile(DaemonConfPath, `\nrpcpassword=${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 = () => { - return new shepherd.Promise((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'); - - shepherd.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 = () => { - return new shepherd.Promise((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'); - - shepherd.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 = () => { - return new shepherd.Promise((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'); - shepherd.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((result) => { - return rpcpass(); - }) - .then(server) - .then(rpcbind) - .then(addnode); - }); - - shepherd.log(result); - shepherd.writeLog(`checkconf addnode ${result}`); - - resolve(result); - }); - } - - CheckFileExists() - .then((result) => { - return FixFilePermissions(); - }) - .then(RemoveLines) - .then(CheckConf); -} - -shepherd.getMaxconKMDConf = () => { - return new shepherd.Promise((resolve, reject) => { - shepherd.fs.readFile(`${shepherd.komodoDir}/komodo.conf`, 'utf8', (err, data) => { - if (err) { - shepherd.log(`kmd conf maxconnections param read failed`); - resolve('unset'); - } else { - const _maxcon = data.match(/maxconnections=\s*(.*)/); - - if (!_maxcon) { - shepherd.log(`kmd conf maxconnections param is unset`); - resolve(false); - } else { - shepherd.log(`kmd conf maxconnections param is already set to ${_maxcon[1]}`); - resolve(_maxcon[1]); - } - } - }); - }); -} - -shepherd.setMaxconKMDConf = (limit) => { - return new shepherd.Promise((resolve, reject) => { - shepherd.fs.readFile(`${shepherd.komodoDir}/komodo.conf`, 'utf8', (err, data) => { - const _maxconVal = limit ? 1 : 10; - - if (err) { - shepherd.log(`error reading ${shepherd.komodoDir}/komodo.conf`); - resolve(false); - } else { - if (data.indexOf('maxconnections=') > -1) { - const _maxcon = data.match(/maxconnections=\s*(.*)/); - - data = data.replace(`maxconnections=${_maxcon[1]}`, `maxconnections=${_maxconVal}`); - } else { - data = `${data}\nmaxconnections=${_maxconVal}\n`; - } - - shepherd.fs.writeFile(`${shepherd.komodoDir}/komodo.conf`, data, (err) => { - if (err) { - shepherd.log(`error writing ${shepherd.komodoDir}/komodo.conf maxconnections=${_maxconVal}`); - resolve(false); - } else { - shepherd.log(`kmd conf maxconnections is set to ${_maxconVal}`); - resolve(true); - } - }); - } - }); - }); -} - -const 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}`); - - switch (shepherd.os.platform()) { - case 'darwin': - nativeCoindDir = `${process.env.HOME}/Library/Application Support/${shepherd.nativeCoindList[coind.toLowerCase()].bin}`; - break; - case 'linux': - nativeCoindDir = coind ? `${process.env.HOME}/.${shepherd.nativeCoindList[coind.toLowerCase()].bin.toLowerCase()}` : null; - break; - case 'win32': - nativeCoindDir = coind ? `${process.env.APPDATA}/${shepherd.nativeCoindList[coind.toLowerCase()].bin}` : null; - break; - } - - switch (flock) { - case 'komodod': - DaemonConfPath = shepherd.komodoDir; - if (shepherd.os.platform() === 'win32') { - DaemonConfPath = shepherd.path.normalize(DaemonConfPath); - shepherd.log('===>>> SHEPHERD API OUTPUT ===>>>'); - } - break; - case 'zcashd': - DaemonConfPath = shepherd.ZcashDir; - if (shepherd.os.platform() === 'win32') { - DaemonConfPath = shepherd.path.normalize(DaemonConfPath); - } - break; - case 'chipsd': - DaemonConfPath = shepherd.chipsDir; - if (shepherd.os.platform() === 'win32') { - DaemonConfPath = shepherd.path.normalize(DaemonConfPath); - } - break; - case 'coind': - DaemonConfPath = shepherd.os.platform() === 'win32' ? shepherd.path.normalize(`${shepherd.coindRootDir}/${coind.toLowerCase()}`) : `${shepherd.coindRootDir}/${coind.toLowerCase()}`; - break; - default: - DaemonConfPath = `${shepherd.komodoDir}/${flock}`; - if (shepherd.os.platform() === 'win32') { - DaemonConfPath = shepherd.path.normalize(DaemonConfPath); - } - } - - shepherd.writeLog(`getconf path: ${DaemonConfPath}`); - shepherd.log(`daemon path: ${DaemonConfPath}`); - - return DaemonConfPath; -} - -const 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 = () => { - const os_data = { - 'totalmem_bytes': shepherd.os.totalmem(), - 'totalmem_readable': formatBytes(shepherd.os.totalmem()), - 'arch': shepherd.os.arch(), - 'cpu': shepherd.os.cpus()[0].model, - 'cpu_cores': shepherd.os.cpus().length, - 'platform': shepherd.os.platform(), - 'os_release': shepherd.os.release(), - 'os_type': shepherd.os.type(), - }; - - return os_data; -} - -shepherd.appInfo = () => { - const sysInfo = shepherd.SystemInfo(); - const releaseInfo = shepherd.appBasicInfo; - const dirs = { - agamaDir: shepherd.agamaDir, - komodoDir: shepherd.komodoDir, - komododBin: shepherd.komododBin, - configLocation: `${shepherd.agamaDir}/config.json`, - cacheLocation: `${shepherd.agamaDir}/shepherd`, - }; - - return { - sysInfo, - releaseInfo, - dirs, - appSession: shepherd.appSessionHash, - }; -} - module.exports = shepherd; \ No newline at end of file diff --git a/routes/shepherd/addCoinShortcuts.js b/routes/shepherd/addCoinShortcuts.js new file mode 100644 index 0000000..7232d37 --- /dev/null +++ b/routes/shepherd/addCoinShortcuts.js @@ -0,0 +1,103 @@ +module.exports = (shepherd) => { + shepherd.startSPV = (coin) => { + if (coin === 'KMD+REVS+JUMBLR') { + shepherd.addElectrumCoin('KMD'); + shepherd.addElectrumCoin('REVS'); + shepherd.addElectrumCoin('JUMBLR'); + } else { + shepherd.addElectrumCoin(coin); + } + } + + shepherd.startKMDNative = (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, + }) + }; + + shepherd.request(options, (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], + }) + }; + + shepherd.request(options, (error, response, body) => { + if (response && + response.statusCode && + response.statusCode === 200) { + //resolve(body); + } else { + //resolve(body); + } + }); + }, 100); + } + } + }; + + return shepherd; +}; \ No newline at end of file diff --git a/routes/shepherd/appInfo.js b/routes/shepherd/appInfo.js new file mode 100644 index 0000000..d22f7b1 --- /dev/null +++ b/routes/shepherd/appInfo.js @@ -0,0 +1,78 @@ +const 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]}`; +} + +module.exports = (shepherd) => { + shepherd.SystemInfo = () => { + const os_data = { + 'totalmem_bytes': shepherd.os.totalmem(), + 'totalmem_readable': formatBytes(shepherd.os.totalmem()), + 'arch': shepherd.os.arch(), + 'cpu': shepherd.os.cpus()[0].model, + 'cpu_cores': shepherd.os.cpus().length, + 'platform': shepherd.os.platform(), + 'os_release': shepherd.os.release(), + 'os_type': shepherd.os.type(), + }; + + return os_data; + } + + shepherd.appInfo = () => { + const sysInfo = shepherd.SystemInfo(); + const releaseInfo = shepherd.appBasicInfo; + const dirs = { + agamaDir: shepherd.agamaDir, + komodoDir: shepherd.komodoDir, + komododBin: shepherd.komododBin, + configLocation: `${shepherd.agamaDir}/config.json`, + cacheLocation: `${shepherd.agamaDir}/shepherd`, + }; + + return { + sysInfo, + releaseInfo, + dirs, + appSession: shepherd.appSessionHash, + }; + } + + /* + * type: GET + * + */ + shepherd.get('/sysinfo', (req, res, next) => { + const obj = shepherd.SystemInfo(); + res.send(obj); + }); + + /* + * type: GET + * + */ + shepherd.get('/appinfo', (req, res, next) => { + const obj = shepherd.appInfo(); + res.send(obj); + }); + + return shepherd; +}; \ No newline at end of file diff --git a/routes/shepherd/auth.js b/routes/shepherd/auth.js new file mode 100644 index 0000000..75ed1c9 --- /dev/null +++ b/routes/shepherd/auth.js @@ -0,0 +1,34 @@ +module.exports = (shepherd) => { + /* + * type: GET + * + */ + shepherd.get('/auth/status', (req, res, next) => { // not finished + let successObj; + let _status = false; + + if (Object.keys(shepherd.coindInstanceRegistry).length) { + if (Object.keys(shepherd.electrumCoins).length > 1 && shepherd.electrumCoins.auth) { + _status = true; + } else if (Object.keys(shepherd.electrumCoins).length === 1 && !shepherd.electrumCoins.auth) { + _status = true; + } + } else if (Object.keys(shepherd.electrumCoins).length > 1 && shepherd.electrumCoins.auth) { + _status = true; + } else if (Object.keys(shepherd.electrumCoins).length === 1 && !Object.keys(shepherd.coindInstanceRegistry).length) { + _status = true; + } + + successObj = { + pubkey: 'nativeonly', + result: 'success', + handle: '', + status: _status ? 'unlocked' : 'locked', + duration: 2507830, + }; + + res.end(JSON.stringify(successObj)); + }); + + return shepherd; +}; \ No newline at end of file diff --git a/routes/shepherd/binsTestUtil.js b/routes/shepherd/binsTestUtil.js new file mode 100644 index 0000000..edc0e42 --- /dev/null +++ b/routes/shepherd/binsTestUtil.js @@ -0,0 +1,239 @@ +module.exports = (shepherd) => { + shepherd.testClearAll = () => { + return new shepherd.Promise((resolve, reject) => { + shepherd.fs.removeSync(`${iguanaTestDir}`); + resolve('done'); + }); + } + + shepherd.testBins = (daemonName) => { + return new shepherd.Promise((resolve, reject) => { + const _bins = { + komodod: shepherd.komododBin, + komodoCli: shepherd.komodocliBin, + }; + const _arg = null; + let _pid; + + shepherd.log('testBins exec ' + _bins[daemonName]); + + if (!shepherd.fs.existsSync(shepherd.agamaTestDir)) { + shepherd.fs.mkdirSync(shepherd.agamaTestDir); + } + + try { + shepherd._fs.access(`${shepherd.agamaTestDir}/${daemonName}Test.log`, shepherd.fs.constants.R_OK, (err) => { + if (!err) { + try { + shepherd._fs.unlinkSync(`${shepherd.agamaTestDir}/${daemonName}Test.log`); + } catch (e) {} + } else { + shepherd.log(`path ${shepherd.agamaTestDir}/${daemonName}Test.log doesnt exist`); + } + }); + } catch (e) {} + + if (daemonName === 'komodod') { + try { + shepherd._fs.access(`${iguanaTestDir}/debug.log`, shepherd.fs.constants.R_OK, (err) => { + if (!err) { + shepherd._fs.unlinkSync(`${iguanaTestDir}/db.log`); + shepherd._fs.unlinkSync(`${iguanaTestDir}/debug.log`); + shepherd._fs.unlinkSync(`${iguanaTestDir}/komodo.conf`); + shepherd._fs.unlinkSync(`${iguanaTestDir}/komodod.pid`); + shepherd._fs.unlinkSync(`${iguanaTestDir}/komodostate`); + shepherd._fs.unlinkSync(`${iguanaTestDir}/realtime`); + shepherd._fs.unlinkSync(`${iguanaTestDir}/wallet.dat`); + shepherd._fs.unlinkSync(`${iguanaTestDir}/.lock`); + shepherd.fs.removeSync(`${iguanaTestDir}/blocks`); + shepherd.fs.removeSync(`${iguanaTestDir}/chainstate`); + shepherd.fs.removeSync(`${iguanaTestDir}/database`); + execKomodod(); + } else { + shepherd.log(`test: nothing to remove in ${iguanaTestDir}`); + execKomodod(); + } + }); + } catch (e) {} + + const 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'; + + shepherd.fs.writeFile(`${iguanaTestDir}/komodo.conf`, _komodoConf, (err) => { + if (err) { + shepherd.log(`test: error writing komodo conf in ${iguanaTestDir}`); + } + }); + + shepherd.portscanner.checkPortStatus('7771', '127.0.0.1', (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: shepherd.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(() => { + const options = { + url: `http://localhost:7771`, + method: 'POST', + auth: { + user: 'user83f3afba8d714993', + pass: '0d4430ca1543833e35bce5a0cc9e16b3', + }, + body: JSON.stringify({ + agent: 'bitcoinrpc', + method: 'getinfo', + }), + }; + + shepherd.request(options, (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(() => { + 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) + + /*shepherd.execFile(`${shepherd.komododBin}`, _arg, { + 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 = (path) => { + return new shepherd.Promise((resolve, reject) => { + if (shepherd.path.indexOf(' ') > -1) { + shepherd.log(`error testing path ${path}`); + resolve(-1); + } else { + shepherd.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); + } + } + }); + } + }); + } + + return shepherd; +}; \ No newline at end of file diff --git a/routes/shepherd/binsUtils.js b/routes/shepherd/binsUtils.js new file mode 100644 index 0000000..c4a7cbe --- /dev/null +++ b/routes/shepherd/binsUtils.js @@ -0,0 +1,70 @@ +module.exports = (shepherd) => { + // osx and linux + shepherd.binFixRights = () => { + const osPlatform = shepherd.os.platform(); + const _bins = [ + shepherd.komododBin, + shepherd.komodocliBin + ]; + + if (osPlatform === 'darwin' || + osPlatform === 'linux') { + for (let i = 0; i < _bins.length; i++) { + shepherd._fs.stat(_bins[i], (err, stat) => { + if (!err) { + if (parseInt(stat.mode.toString(8), 10) !== 100775) { + shepherd.log(`${_bins[i]} fix permissions`); + shepherd.fsnode.chmodSync(_bins[i], '0775'); + } + } else { + shepherd.log(`error: ${_bins[i]} not found`); + } + }); + } + } + } + + shepherd.killRogueProcess = (processName) => { + // kill rogue process copies on start + let processGrep; + const osPlatform = shepherd.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; + } + + shepherd.exec(processGrep, (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)`); + + shepherd.exec(pkillCmd, (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}`); + }; + }); + } + + return shepherd; +}; \ No newline at end of file diff --git a/routes/shepherd/coins.js b/routes/shepherd/coins.js new file mode 100644 index 0000000..f74e778 --- /dev/null +++ b/routes/shepherd/coins.js @@ -0,0 +1,31 @@ +module.exports = (shepherd) => { + /* + * type: GET + * + */ + shepherd.get('/InstantDEX/allcoins', (req, res, next) => { + let successObj; + let nativeCoindList = []; + let electrumCoinsList = []; + + for (let key in shepherd.electrumCoins) { + if (key !== 'auth') { + electrumCoinsList.push(shepherd.electrumCoins[key].abbr); + } + } + + for (let key in shepherd.coindInstanceRegistry) { + nativeCoindList.push(key === 'komodod' ? 'KMD' : key); + } + + successObj = { + native: nativeCoindList, + spv: electrumCoinsList, + total: Object.keys(shepherd.electrumCoins).length - 1 + Object.keys(nativeCoindList).length, + }; + + res.end(JSON.stringify(successObj)); + }); + + return shepherd; +}; \ No newline at end of file diff --git a/routes/shepherd/coinsList.js b/routes/shepherd/coinsList.js new file mode 100644 index 0000000..dfe13e1 --- /dev/null +++ b/routes/shepherd/coinsList.js @@ -0,0 +1,71 @@ +module.exports = (shepherd) => { + /* + * type: GET + * + */ + shepherd.get('/coinslist', (req, res, next) => { + if (shepherd.fs.existsSync(`${shepherd.agamaDir}/shepherd/coinslist.json`)) { + shepherd.fs.readFile(`${shepherd.agamaDir}/shepherd/coinslist.json`, 'utf8', (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('/coinslist', (req, res, next) => { + const _payload = req.body.payload; + + if (!_payload) { + const errorObj = { + msg: 'error', + result: 'no payload provided', + }; + + res.end(JSON.stringify(errorObj)); + } else { + shepherd.fs.writeFile(`${shepherd.agamaDir}/shepherd/coinslist.json`, JSON.stringify(_payload), (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)); + } + }); + } + }); + + return shepherd; +}; \ No newline at end of file diff --git a/routes/shepherd/confMaxconnections.js b/routes/shepherd/confMaxconnections.js new file mode 100644 index 0000000..971c06c --- /dev/null +++ b/routes/shepherd/confMaxconnections.js @@ -0,0 +1,55 @@ +module.exports = (shepherd) => { + shepherd.getMaxconKMDConf = () => { + return new shepherd.Promise((resolve, reject) => { + shepherd.fs.readFile(`${shepherd.komodoDir}/komodo.conf`, 'utf8', (err, data) => { + if (err) { + shepherd.log(`kmd conf maxconnections param read failed`); + resolve('unset'); + } else { + const _maxcon = data.match(/maxconnections=\s*(.*)/); + + if (!_maxcon) { + shepherd.log(`kmd conf maxconnections param is unset`); + resolve(false); + } else { + shepherd.log(`kmd conf maxconnections param is already set to ${_maxcon[1]}`); + resolve(_maxcon[1]); + } + } + }); + }); + } + + shepherd.setMaxconKMDConf = (limit) => { + return new shepherd.Promise((resolve, reject) => { + shepherd.fs.readFile(`${shepherd.komodoDir}/komodo.conf`, 'utf8', (err, data) => { + const _maxconVal = limit ? 1 : 10; + + if (err) { + shepherd.log(`error reading ${shepherd.komodoDir}/komodo.conf`); + resolve(false); + } else { + if (data.indexOf('maxconnections=') > -1) { + const _maxcon = data.match(/maxconnections=\s*(.*)/); + + data = data.replace(`maxconnections=${_maxcon[1]}`, `maxconnections=${_maxconVal}`); + } else { + data = `${data}\nmaxconnections=${_maxconVal}\n`; + } + + shepherd.fs.writeFile(`${shepherd.komodoDir}/komodo.conf`, data, (err) => { + if (err) { + shepherd.log(`error writing ${shepherd.komodoDir}/komodo.conf maxconnections=${_maxconVal}`); + resolve(false); + } else { + shepherd.log(`kmd conf maxconnections is set to ${_maxconVal}`); + resolve(true); + } + }); + } + }); + }); + } + + return shepherd; +}; \ No newline at end of file diff --git a/routes/shepherd/config.js b/routes/shepherd/config.js new file mode 100644 index 0000000..6d4f06c --- /dev/null +++ b/routes/shepherd/config.js @@ -0,0 +1,152 @@ +module.exports = (shepherd) => { + shepherd.loadLocalConfig = () => { + if (shepherd.fs.existsSync(`${shepherd.agamaDir}/config.json`)) { + let localAppConfig = shepherd.fs.readFileSync(`${shepherd.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 = (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 = (appSettings) => { + let appConfFileName = `${shepherd.agamaDir}/config.json`; + + shepherd._fs.access(shepherd.agamaDir, shepherd.fs.constants.R_OK, (err) => { + if (!err) { + + const FixFilePermissions = () => { + return new shepherd.Promise((resolve, reject) => { + const result = 'config.json file permissions updated to Read/Write'; + + shepherd.fsnode.chmodSync(appConfFileName, '0666'); + + setTimeout(() => { + shepherd.log(result); + shepherd.writeLog(result); + resolve(result); + }, 1000); + }); + } + + const FsWrite = () => { + return new shepherd.Promise((resolve, reject) => { + const result = 'config.json write file is done'; + + shepherd.fs.writeFile(appConfFileName, + JSON.stringify(appSettings) + .replace(/,/g, ',\n') // format json in human readable form + .replace(/":/g, '": ') + .replace(/{/g, '{\n') + .replace(/}/g, '\n}'), 'utf8', (err) => { + if (err) + return shepherd.log(err); + }); + + shepherd.fsnode.chmodSync(appConfFileName, '0666'); + setTimeout(() => { + shepherd.log(result); + shepherd.log(`app conf.json file is created successfully at: ${shepherd.agamaDir}`); + shepherd.writeLog(`app conf.json file is created successfully at: ${shepherd.agamaDir}`); + resolve(result); + }, 2000); + }); + } + + FsWrite() + .then(FixFilePermissions()); + } + }); + } + + /* + * type: POST + * params: payload + */ + shepherd.post('/appconf', (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', (req, res, next) => { + shepherd.saveLocalAppConf(shepherd.defaultAppConfig); + + const successObj = { + msg: 'success', + result: 'config saved', + }; + + res.end(JSON.stringify(successObj)); + }); + + /* + * type: GET + * + */ + shepherd.get('/appconf', (req, res, next) => { + const obj = shepherd.loadLocalConfig(); + res.send(obj); + }); + + return shepherd; +}; \ No newline at end of file diff --git a/routes/shepherd/daemonControl.js b/routes/shepherd/daemonControl.js new file mode 100644 index 0000000..aa0b7ad --- /dev/null +++ b/routes/shepherd/daemonControl.js @@ -0,0 +1,868 @@ +module.exports = (shepherd) => { + const 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}`); + + switch (shepherd.os.platform()) { + case 'darwin': + nativeCoindDir = `${process.env.HOME}/Library/Application Support/${shepherd.nativeCoindList[coind.toLowerCase()].bin}`; + break; + case 'linux': + nativeCoindDir = coind ? `${process.env.HOME}/.${shepherd.nativeCoindList[coind.toLowerCase()].bin.toLowerCase()}` : null; + break; + case 'win32': + nativeCoindDir = coind ? `${process.env.APPDATA}/${shepherd.nativeCoindList[coind.toLowerCase()].bin}` : null; + break; + } + + switch (flock) { + case 'komodod': + DaemonConfPath = shepherd.komodoDir; + if (shepherd.os.platform() === 'win32') { + DaemonConfPath = shepherd.path.normalize(DaemonConfPath); + shepherd.log('===>>> SHEPHERD API OUTPUT ===>>>'); + } + break; + case 'zcashd': + DaemonConfPath = shepherd.ZcashDir; + if (shepherd.os.platform() === 'win32') { + DaemonConfPath = shepherd.path.normalize(DaemonConfPath); + } + break; + case 'chipsd': + DaemonConfPath = shepherd.chipsDir; + if (shepherd.os.platform() === 'win32') { + DaemonConfPath = shepherd.path.normalize(DaemonConfPath); + } + break; + case 'coind': + DaemonConfPath = shepherd.os.platform() === 'win32' ? shepherd.path.normalize(`${shepherd.coindRootDir}/${coind.toLowerCase()}`) : `${shepherd.coindRootDir}/${coind.toLowerCase()}`; + break; + default: + DaemonConfPath = `${shepherd.komodoDir}/${flock}`; + if (shepherd.os.platform() === 'win32') { + DaemonConfPath = shepherd.path.normalize(DaemonConfPath); + } + } + + shepherd.writeLog(`getconf path: ${DaemonConfPath}`); + shepherd.log(`daemon path: ${DaemonConfPath}`); + + return DaemonConfPath; + } + + // TODO: json.stringify wrapper + + const herder = (flock, data, coind) => { + if (data === undefined) { + data = 'none'; + shepherd.log('it is undefined'); + } + + shepherd.log(`herder flock: ${flock} coind: ${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' ? `${shepherd.komodoDir}/${data.ac_name}` : shepherd.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}`); + + // datadir case, check if komodo/chain folder exists + if (shepherd.appConfig.dataDir.length && + data.ac_name !== 'komodod') { + const _dir = data.ac_name !== 'komodod' ? `${shepherd.komodoDir}/${data.ac_name}` : shepherd.komodoDir; + + try { + shepherd._fs.accessSync(_dir, shepherd.fs.R_OK | shepherd.fs.W_OK); + + shepherd.log(`komodod datadir ${_dir} exists`); + } catch (e) { + shepherd.log(`komodod datadir ${_dir} access err: ${e}`); + shepherd.log(`attempting to create komodod datadir ${_dir}`); + + shepherd.fs.mkdirSync(_dir); + + if (shepherd.fs.existsSync(_dir)) { + shepherd.log(`created komodod datadir folder at ${_dir}`); + } else { + shepherd.log(`unable to create komodod datadir folder at ${_dir}`); + } + } + } + + // truncate debug.log + if (!shepherd.kmdMainPassiveMode) { + try { + const _confFileAccess = shepherd._fs.accessSync(kmdDebugLogLocation, shepherd.fs.R_OK | shepherd.fs.W_OK); + + if (_confFileAccess) { + shepherd.log(`error accessing ${kmdDebugLogLocation}`); + shepherd.writeLog(`error accessing ${kmdDebugLogLocation}`); + } else { + try { + shepherd.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 = shepherd.assetChainPorts[data.ac_name]; + + try { + // check if komodod instance is already running + shepherd.portscanner.checkPortStatus(_port, '127.0.0.1', (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 !== 'komodod' ? '/' + data.ac_name : ''); + } + + shepherd.log(`exec ${shepherd.komododBin} ${data.ac_options.join(' ')}${_customParam}`); + shepherd.writeLog(`exec ${shepherd.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}`); + + shepherd.coindInstanceRegistry[data.ac_name] = true; + if (!shepherd.kmdMainPassiveMode) { + let _arg = `${coindACParam}${data.ac_options.join(' ')}${_customParam}`; + _arg = _arg.trim().split(' '); + shepherd.execFile(`${shepherd.komododBin}`, _arg, { + maxBuffer: 1024 * 1000000 // 1000 mb + }, (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) { + shepherd.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}`); + } + } + + // TODO: refactor + if (flock === 'chipsd') { + let kmdDebugLogLocation = `${shepherd.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 = shepherd._fs.accessSync(kmdDebugLogLocation, shepherd.fs.R_OK | shepherd.fs.W_OK); + + if (_confFileAccess) { + shepherd.log(`error accessing ${kmdDebugLogLocation}`); + shepherd.writeLog(`error accessing ${kmdDebugLogLocation}`); + } else { + try { + shepherd.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 = shepherd.assetChainPorts.chipsd; + + try { + // check if komodod instance is already running + shepherd.portscanner.checkPortStatus(_port, '127.0.0.1', (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 ${shepherd.chipsBin} ${_customParam}`); + shepherd.writeLog(`exec ${shepherd.chipsBin} ${_customParam}`); + + shepherd.log(`daemon param ${data.ac_custom_param}`); + + shepherd.coindInstanceRegistry['CHIPS'] = true; + let _arg = `${_customParam}`; + _arg = _arg.trim().split(' '); + + if (_arg && + _arg.length > 1) { + shepherd.execFile(`${shepherd.chipsBin}`, _arg, { + maxBuffer: 1024 * 1000000 // 1000 mb + }, (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 { + shepherd.execFile(`${shepherd.chipsBin}`, { + maxBuffer: 1024 * 1000000 // 1000 mb + }, (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 = `${shepherd.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: shepherd.zcashdBin, // path to binary + name: data.ac_name, // REVS, USD, EUR etc. + exec_mode: 'fork', + cwd: shepherd.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 = shepherd.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 { + shepherd._fs.access(coindDebugLogLocation, shepherd.fs.constants.R_OK, (err) => { + if (err) { + shepherd.log(`error accessing ${coindDebugLogLocation}`); + shepherd.writeLog(`error accessing ${coindDebugLogLocation}`); + } else { + shepherd.log(`truncate ${coindDebugLogLocation}`); + shepherd.writeLog(`truncate ${coindDebugLogLocation}`); + shepherd.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 = `${shepherd.coindRootDir}/${coind.toLowerCase()}/${shepherd.nativeCoindList[coind.toLowerCase()].bin.toLowerCase()}d`; + + try { + // check if coind instance is already running + shepherd.portscanner.checkPortStatus(_port, '127.0.0.1', (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(' ')}`); + + shepherd.coindInstanceRegistry[coind] = true; + let _arg = `${data.ac_options.join(' ')}`; + _arg = _arg.trim().split(' '); + shepherd.execFile(`${coindBin}`, _arg, { + maxBuffer: 1024 * 1000000 // 1000 mb + }, (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}`); + } + } + } + + const setConf = (flock, coind) => { + let nativeCoindDir; + let DaemonConfPath; + + shepherd.log(flock); + shepherd.writeLog(`setconf ${flock}`); + + if (shepherd.os.platform() === 'darwin') { + nativeCoindDir = coind ? `${process.env.HOME}/Library/Application Support/${shepherd.nativeCoindList[coind.toLowerCase()].bin}` : null; + } + + if (shepherd.os.platform() === 'linux') { + nativeCoindDir = coind ? `${process.env.HOME}/.${shepherd.nativeCoindList[coind.toLowerCase()].bin.toLowerCase()}` : null; + } + + if (shepherd.os.platform() === 'win32') { + nativeCoindDir = coind ? `${process.env.APPDATA}/${shepherd.nativeCoindList[coind.toLowerCase()].bin}` : null; + } + + switch (flock) { + case 'komodod': + DaemonConfPath = `${shepherd.komodoDir}/komodo.conf`; + + if (shepherd.os.platform() === 'win32') { + DaemonConfPath = shepherd.path.normalize(DaemonConfPath); + } + break; + case 'zcashd': + DaemonConfPath = `${shepherd.ZcashDir}/zcash.conf`; + + if (shepherd.os.platform() === 'win32') { + DaemonConfPath = shepherd.path.normalize(DaemonConfPath); + } + break; + case 'chipsd': + DaemonConfPath = `${shepherd.chipsDir}/chips.conf`; + + if (shepherd.os.platform() === 'win32') { + DaemonConfPath = shepherd.path.normalize(DaemonConfPath); + } + break; + case 'coind': + DaemonConfPath = `${nativeCoindDir}/${shepherd.nativeCoindList[coind.toLowerCase()].bin.toLowerCase()}.conf`; + + if (shepherd.os.platform() === 'win32') { + DaemonConfPath = shepherd.path.normalize(DaemonConfPath); + } + break; + default: + DaemonConfPath = `${shepherd.komodoDir}/${flock}/${flock}.conf`; + + if (shepherd.os.platform() === 'win32') { + DaemonConfPath = shepherd.path.normalize(DaemonConfPath); + } + } + + shepherd.log(DaemonConfPath); + shepherd.writeLog(`setconf ${DaemonConfPath}`); + + const CheckFileExists = () => { + return new shepherd.Promise((resolve, reject) => { + const result = 'Check Conf file exists is done'; + + const confFileExist = shepherd.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 = () => { + return new shepherd.Promise((resolve, reject) => { + const result = 'Conf file permissions updated to Read/Write'; + + shepherd.fsnode.chmodSync(DaemonConfPath, '0666'); + shepherd.log(result); + shepherd.writeLog(`setconf ${result}`); + + resolve(result); + }); + } + + const RemoveLines = () => { + return new shepherd.Promise((resolve, reject) => { + const result = 'RemoveLines is done'; + + shepherd.fs.readFile(DaemonConfPath, 'utf8', (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'); + + shepherd.fs.writeFile(DaemonConfPath, rmlines, 'utf8', (err) => { + if (err) + return shepherd.log(err); + + shepherd.fsnode.chmodSync(DaemonConfPath, '0666'); + shepherd.writeLog(`setconf ${result}`); + shepherd.log(result); + resolve(result); + }); + }); + }); + } + + const CheckConf = () => { + return new shepherd.Promise((resolve, reject) => { + const result = 'CheckConf is done'; + + shepherd.setconf.status(DaemonConfPath, (err, status) => { + const rpcuser = () => { + return new shepherd.Promise((resolve, reject) => { + const result = 'checking rpcuser...'; + + if (status[0].hasOwnProperty('rpcuser')) { + shepherd.log('rpcuser: OK'); + shepherd.writeLog('rpcuser: OK'); + } else { + const randomstring = shepherd.md5((Math.random() * Math.random() * 999).toString()); + + shepherd.log('rpcuser: NOT FOUND'); + shepherd.writeLog('rpcuser: NOT FOUND'); + + shepherd.fs.appendFile(DaemonConfPath, `\nrpcuser=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 = () => { + return new shepherd.Promise((resolve, reject) => { + const result = 'checking rpcpassword...'; + + if (status[0].hasOwnProperty('rpcpassword')) { + shepherd.log('rpcpassword: OK'); + shepherd.writeLog('rpcpassword: OK'); + } else { + const randomstring = shepherd.md5((Math.random() * Math.random() * 999).toString()); + + shepherd.log('rpcpassword: NOT FOUND'); + shepherd.writeLog('rpcpassword: NOT FOUND'); + + shepherd.fs.appendFile(DaemonConfPath, `\nrpcpassword=${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 = () => { + return new shepherd.Promise((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'); + + shepherd.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 = () => { + return new shepherd.Promise((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'); + + shepherd.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 = () => { + return new shepherd.Promise((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'); + shepherd.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((result) => { + return rpcpass(); + }) + .then(server) + .then(rpcbind) + .then(addnode); + }); + + shepherd.log(result); + shepherd.writeLog(`checkconf addnode ${result}`); + + resolve(result); + }); + } + + CheckFileExists() + .then((result) => { + return FixFilePermissions(); + }) + .then(RemoveLines) + .then(CheckConf); + } + /* + * type: POST + * params: herd + */ + shepherd.post('/herd', (req, res) => { + shepherd.log('======= req.body ======='); + shepherd.log(req.body); + + if (req.body.options && + !shepherd.kmdMainPassiveMode) { + const testCoindPort = (skipError) => { + if (!shepherd.lockDownAddCoin) { + const _port = shepherd.assetChainPorts[req.body.options.ac_name]; + + shepherd.portscanner.checkPortStatus(_port, '127.0.0.1', (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(() => { + 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', (req, res) => { + shepherd.log('======= req.body ======='); + shepherd.log(req.body); + + if (shepherd.os.platform() === 'win32' && + req.body.chain == 'komodod') { + setkomodoconf = spawn(shepherd.path.join(__dirname, '../assets/bin/win64/genkmdconf.bat')); + } else { + shepherd.setConf(req.body.chain); + } + + const obj = { + msg: 'success', + result: 'result', + }; + + res.end(JSON.stringify(obj)); + }); + + /* + * type: POST + */ + shepherd.post('/getconf', (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)); + }); + + shepherd.setConfKMD = (isChips) => { + // check if kmd conf exists + shepherd._fs.access(isChips ? `${shepherd.chipsDir}/chips.conf` : `${shepherd.komodoDir}/komodo.conf`, shepherd.fs.constants.R_OK, (err) => { + if (err) { + shepherd.log(isChips ? 'creating chips conf' : 'creating komodo conf'); + shepherd.writeLog(isChips ? `creating chips conf in ${shepherd.chipsDir}/chips.conf` : `creating komodo conf in ${shepherd.komodoDir}/komodo.conf`); + setConf(isChips ? 'chipsd' : 'komodod'); + } else { + const _confSize = shepherd.fs.lstatSync(isChips ? `${shepherd.chipsDir}/chips.conf` : `${shepherd.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 ${shepherd.chipsDir}/chips.conf` : `creating komodo conf in ${shepherd.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'); + } + } + }); + } + + return shepherd; +}; \ No newline at end of file diff --git a/routes/shepherd/dashboardUpdate.js b/routes/shepherd/dashboardUpdate.js new file mode 100644 index 0000000..bc0e521 --- /dev/null +++ b/routes/shepherd/dashboardUpdate.js @@ -0,0 +1,294 @@ +module.exports = (shepherd) => { + /* + * Combined native dashboard update same as in gui + * type: GET + * params: coin + */ + shepherd.post('/native/dashboard/update', (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' + ]; + } + + const getAddressesNative = (coin) => { + const type = [ + 'public', + 'private' + ]; + + if (coin === 'CHIPS') { + type.pop(); + } + + shepherd.Promise.all(type.map((_type, index) => { + return new shepherd.Promise((resolve, reject) => { + _bitcoinRPC( + coin, + _type === 'public' ? 'getaddressesbyaccount' : 'z_listaddresses', + [''] + ).then((_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) { + const 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((elem, pos) => { + return result[0].indexOf(elem) === pos; + }); + } + if (result[1] && + result[1].length) { + result[1] = result[1].filter((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) { + shepherd.Promise.all(result[1].map((_address, index) => { + return new shepherd.Promise((resolve, reject) => { + _bitcoinRPC(coin, 'z_getbalance', [_address]) + .then((__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((__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)); + } + }) + } + + const _bitcoinRPC = (coin, cmd, params) => { + return new shepherd.Promise((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, + }; + + shepherd.request(options, (error, response, body) => { + if (response && + response.statusCode && + response.statusCode === 200) { + resolve(body); + } else { + resolve(body); + } + }); + }); + } + + shepherd.Promise.all(_promiseStack.map((_call, index) => { + let _params; + if (_call === 'listtransactions') { + _params = [ + '', + 300, + 0 + ]; + } + return new shepherd.Promise((resolve, reject) => { + _bitcoinRPC( + _coin, + _call, + _params + ) + .then((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); + }); + }); + + return shepherd; +}; \ No newline at end of file diff --git a/routes/shepherd/debugLog.js b/routes/shepherd/debugLog.js new file mode 100644 index 0000000..74c41c1 --- /dev/null +++ b/routes/shepherd/debugLog.js @@ -0,0 +1,92 @@ +module.exports = (shepherd) => { + /* + * type: GET + * params: herd, lastLines + */ + shepherd.post('/debuglog', (req, res) => { + let _herd = req.body.herdname; + let _ac = req.body.ac; + let _lastNLines = req.body.lastLines; + let _location; + + if (shepherd.os.platform() === 'darwin') { + shepherd.komodoDir = shepherd.appConfig.dataDir.length ? shepherd.appConfig.dataDir : `${process.env.HOME}/Library/Application Support/Komodo`; + } + + if (shepherd.os.platform() === 'linux') { + shepherd.komodoDir = shepherd.appConfig.dataDir.length ? shepherd.appConfig.dataDir : `${process.env.HOME}/.komodo`; + } + + if (shepherd.os.platform() === 'win32') { + shepherd.komodoDir = shepherd.appConfig.dataDir.length ? shepherd.appConfig.dataDir : `${process.env.APPDATA}/Komodo`; + shepherd.komodoDir = shepherd.path.normalize(shepherd.komodoDir); + } + + if (_herd === 'komodo') { + _location = shepherd.komodoDir; + } + + if (_ac) { + _location = `${shepherd.komodoDir}/${_ac}`; + + if (_ac === 'CHIPS') { + _location = shepherd.chipsDir; + } + } + + shepherd.readDebugLog(`${_location}/debug.log`, _lastNLines) + .then((result) => { + const _obj = { + msg: 'success', + result: result, + }; + + res.end(JSON.stringify(_obj)); + }, (result) => { + const _obj = { + msg: 'error', + result: result, + }; + + res.end(JSON.stringify(_obj)); + }); + }); + + + shepherd.readDebugLog = (fileLocation, lastNLines) => { + return new shepherd.Promise( + (resolve, reject) => { + if (lastNLines) { + try { + shepherd._fs.access(fileLocation, shepherd.fs.constants.R_OK, (err) => { + if (err) { + shepherd.log(`error reading ${fileLocation}`); + shepherd.writeLog(`error reading ${fileLocation}`); + reject(`readDebugLog error: ${err}`); + } else { + shepherd.log(`reading ${fileLocation}`); + shepherd._fs.readFile(fileLocation, 'utf-8', (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!'); + } + } + ); + }; + + return shepherd; +}; \ No newline at end of file diff --git a/routes/shepherd/dex/coind.js b/routes/shepherd/dex/coind.js new file mode 100644 index 0000000..1747b82 --- /dev/null +++ b/routes/shepherd/dex/coind.js @@ -0,0 +1,44 @@ +module.exports = (shepherd) => { + /* + * list native coind + * type: + * params: + */ + shepherd.get('/coind/list', (req, res, next) => { + const successObj = { + msg: 'success', + result: shepherd.nativeCoindList, + }; + + res.end(JSON.stringify(successObj)); + }); + + shepherd.scanNativeCoindBins = () => { + let nativeCoindList = {}; + + // check if coind bins are present in agama + for (let key in shepherd.nativeCoind) { + nativeCoindList[key] = { + name: shepherd.nativeCoind[key].name, + port: shepherd.nativeCoind[key].port, + bin: shepherd.nativeCoind[key].bin, + bins: { + daemon: false, + cli: false, + }, + }; + + if (shepherd.fs.existsSync(`${shepherd.coindRootDir}/${key}/${shepherd.nativeCoind[key].bin}d${shepherd.os.platform() === 'win32' ? '.exe' : ''}`)) { + nativeCoindList[key].bins.daemon = true; + } + + if (shepherd.fs.existsSync(`${shepherd.coindRootDir}/${key}/${shepherd.nativeCoind[key].bin}-cli${shepherd.os.platform() === 'win32' ? '.exe' : ''}`)) { + nativeCoindList[key].bins.cli = true; + } + } + + return nativeCoindList; + } + + return shepherd; +}; \ No newline at end of file diff --git a/routes/shepherd/downloadBins.js b/routes/shepherd/downloadBins.js new file mode 100644 index 0000000..c3e48da --- /dev/null +++ b/routes/shepherd/downloadBins.js @@ -0,0 +1,165 @@ +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 = []; + +module.exports = (shepherd) => { + /* + * Check bins file size + * type: + * params: + */ + // TODO: promises + shepherd.get('/update/bins/check', (req, res, next) => { + const rootLocation = shepherd.path.join(__dirname, '../../'); + const successObj = { + msg: 'success', + result: 'bins', + }; + + res.end(JSON.stringify(successObj)); + + const _os = shepherd.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++) { + shepherd.remoteFileSize(remoteBinLocation[_os] + latestBins[_os][i], (err, remoteBinSize) => { + const localBinSize = shepherd.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', (req, res, next) => { + const rootLocation = shepherd.path.join(__dirname, '../../'); + const _os = shepherd.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++) { + shepherd.downloadFile({ + remoteFile: remoteBinLocation[_os] + binsToUpdate[i].name, + localFile: `${rootLocation}${localBinLocation[_os]}patch/${binsToUpdate[i].name}`, + onProgress: (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(() => { + // verify that remote file is matching to DL'ed file + const localBinSize = shepherd.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!`); + } + }); + } + }); + + return shepherd; +}; \ No newline at end of file diff --git a/routes/shepherd/downloadPatch.js b/routes/shepherd/downloadPatch.js new file mode 100644 index 0000000..f8be045 --- /dev/null +++ b/routes/shepherd/downloadPatch.js @@ -0,0 +1,154 @@ +module.exports = (shepherd) => { + /* + * DL app patch + * type: GET + * params: patchList + */ + shepherd.get('/update/patch', (req, res, next) => { + const successObj = { + msg: 'success', + result: 'dl started' + }; + + res.end(JSON.stringify(successObj)); + + shepherd.updateAgama(); + }); + + shepherd.updateAgama = () => { + const rootLocation = shepherd.path.join(__dirname, '../../'); + + shepherd.downloadFile({ + remoteFile: 'https://github.com/pbca26/dl-test/raw/master/patch.zip', + localFile: `${rootLocation}patch.zip`, + onProgress: (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(() => { + shepherd.remoteFileSize('https://github.com/pbca26/dl-test/raw/master/patch.zip', (err, remotePatchSize) => { + // verify that remote file is matching to DL'ed file + const localPatchSize = shepherd.fs.statSync(`${rootLocation}patch.zip`).size; + shepherd.log('compare dl file size'); + + if (localPatchSize === remotePatchSize) { + const zip = new shepherd.AdmZip(`${rootLocation}patch.zip`); + + shepherd.log('patch succesfully downloaded'); + shepherd.log('extracting contents'); + + if (shepherd.appConfig.dev) { + if (!shepherd.fs.existsSync(`${rootLocation}/patch`)) { + shepherd.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', + }, + }); + shepherd.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', (req, res, next) => { + const rootLocation = shepherd.path.join(__dirname, '../../'); + const options = { + url: 'https://github.com/pbca26/dl-test/raw/master/version', + method: 'GET', + }; + + shepherd.request(options, (error, response, body) => { + if (response && + response.statusCode && + response.statusCode === 200) { + const remoteVersion = body.split('\n'); + const localVersionFile = shepherd.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', (req, res, next) => { + const dlLocation = shepherd.path.join(__dirname, '../../'); + const zip = new shepherd.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)); + }); + + return shepherd; +}; \ No newline at end of file diff --git a/routes/shepherd/downloadUtil.js b/routes/shepherd/downloadUtil.js new file mode 100644 index 0000000..dfc4a96 --- /dev/null +++ b/routes/shepherd/downloadUtil.js @@ -0,0 +1,49 @@ +module.exports = (shepherd) => { + /** + * Promise based download file method + */ + shepherd.downloadFile = (configuration) => { + return new shepherd.Promise((resolve, reject) => { + // Save variable to know progress + let receivedBytes = 0; + let totalBytes = 0; + + let req = shepherd.request({ + method: 'GET', + uri: configuration.remoteFile, + agentOptions: { + keepAlive: true, + keepAliveMsecs: 15000, + }, + }); + + let out = shepherd.fs.createWriteStream(configuration.localFile); + req.pipe(out); + + req.on('response', (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', (chunk) => { + // Update the received bytes + receivedBytes += chunk.length; + configuration.onProgress(receivedBytes, totalBytes); + }); + } else { + req.on('data', (chunk) => { + // Update the received bytes + receivedBytes += chunk.length; + }); + } + + req.on('end', () => { + resolve(); + }); + }); + } + + return shepherd; +}; \ No newline at end of file diff --git a/routes/shepherd/downloadZcparams.js b/routes/shepherd/downloadZcparams.js new file mode 100644 index 0000000..831d5c1 --- /dev/null +++ b/routes/shepherd/downloadZcparams.js @@ -0,0 +1,127 @@ +module.exports = (shepherd) => { + shepherd.zcashParamsDownloadLinks = { + 'agama.komodoplatform.com': { + proving: 'https://agama.komodoplatform.com/file/supernet/sprout-proving.key', + verifying: 'https://agama.komodoplatform.com/file/supernet/sprout-verifying.key', + }, + 'agama-yq0ysrdtr.stackpathdns.com': { + proving: 'http://agama-yq0ysrdtr.stackpathdns.com/file/supernet/sprout-proving.key', + verifying: 'http://agama-yq0ysrdtr.stackpathdns.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.zcashParamsExist = () => { + let _checkList = { + rootDir: shepherd._fs.existsSync(shepherd.zcashParamsDir), + provingKey: shepherd._fs.existsSync(`${shepherd.zcashParamsDir}/sprout-proving.key`), + provingKeySize: false, + verifyingKey: shepherd._fs.existsSync(`${shepherd.zcashParamsDir}/sprout-verifying.key`), + verifyingKeySize: false, + errors: false, + }; + + if (_checkList.rootDir && + _checkList.provingKey || + _checkList.verifyingKey) { + // verify each key size + const _provingKeySize = _checkList.provingKey ? shepherd.fs.lstatSync(`${shepherd.zcashParamsDir}/sprout-proving.key`) : 0; + const _verifyingKeySize = _checkList.verifyingKey ? shepherd.fs.lstatSync(`${shepherd.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; + } + + /* + * Update bins + * type: + * params: + */ + shepherd.get('/zcparamsdl', (req, res, next) => { + // const dlLocation = shepherd.zcashParamsDir + '/test'; + const dlLocation = shepherd.zcashParamsDir; + const dlOption = req.query.dloption; + + const successObj = { + msg: 'success', + result: 'zcash params dl started', + }; + + res.end(JSON.stringify(successObj)); + + for (let key in shepherd.zcashParamsDownloadLinks[dlOption]) { + shepherd.downloadFile({ + remoteFile: shepherd.zcashParamsDownloadLinks[dlOption][key], + localFile: `${dlLocation}/sprout-${key}.key`, + onProgress: (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(() => { + 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`); + } + }); + } + }); + + return shepherd; +}; \ No newline at end of file diff --git a/routes/shepherd/electrum/auth.js b/routes/shepherd/electrum/auth.js new file mode 100644 index 0000000..691ed94 --- /dev/null +++ b/routes/shepherd/electrum/auth.js @@ -0,0 +1,38 @@ +module.exports = (shepherd) => { + shepherd.get('/electrum/login', (req, res, next) => { + for (let key in shepherd.electrumServers) { + const _abbr = shepherd.electrumServers[key].abbr; + const { priv, pub } = shepherd.seedToWif(req.query.seed, shepherd.findNetworkObj(_abbr), req.query.iguana); + + shepherd.electrumKeys[_abbr] = { + priv, + pub, + }; + } + + shepherd.electrumCoins.auth = true; + + shepherd.log(JSON.stringify(shepherd.electrumKeys, null, '\t'), true); + + const successObj = { + msg: 'success', + result: 'true', + }; + + res.end(JSON.stringify(successObj)); + }); + + shepherd.get('/electrum/dev/logout', (req, res, next) => { + shepherd.electrumCoins.auth = false; + shepherd.electrumKeys = {}; + + const successObj = { + msg: 'success', + result: 'true', + }; + + res.end(JSON.stringify(successObj)); + }); + + return shepherd; +}; \ No newline at end of file diff --git a/routes/shepherd/electrum/balance.js b/routes/shepherd/electrum/balance.js new file mode 100644 index 0000000..9291a7f --- /dev/null +++ b/routes/shepherd/electrum/balance.js @@ -0,0 +1,145 @@ +module.exports = (shepherd) => { + shepherd.get('/electrum/getbalance', (req, res, next) => { + const network = req.query.network || shepherd.findNetworkObj(req.query.coin); + const ecl = new shepherd.electrumJSCore(shepherd.electrumServers[network].port, shepherd.electrumServers[network].address, shepherd.electrumServers[network].proto); // tcp or tls + + shepherd.log('electrum getbalance =>', true); + + 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++) { + shepherd.log(`utxo ${utxoList[i]['tx_hash']} sats ${utxoList[i].value} value ${Number(utxoList[i].value) * 0.00000001}`, true); + + if (Number(utxoList[i].value) * 0.00000001 >= 10) { + _utxo.push(utxoList[i]); + } + } + + shepherd.log('filtered utxo list =>', true); + shepherd.log(_utxo, true); + + if (_utxo && + _utxo.length) { + let interestTotal = 0; + + shepherd.Promise.all(_utxo.map((_utxoItem, index) => { + return new shepherd.Promise((resolve, reject) => { + ecl.blockchainTransactionGet(_utxoItem['tx_hash']) + .then((_rawtxJSON) => { + shepherd.log('electrum gettransaction ==>', true); + shepherd.log((index + ' | ' + (_rawtxJSON.length - 1)), true); + shepherd.log(_rawtxJSON, true); + + // decode tx + const _network = shepherd.getNetworkData(network); + const decodedTx = shepherd.electrumJSTxDecoder(_rawtxJSON, _network); + + if (decodedTx && + decodedTx.format && + decodedTx.format.locktime > 0) { + interestTotal += shepherd.kmdCalcInterest(decodedTx.format.locktime, _utxoItem.value); + } + + shepherd.log('decoded tx =>', true); + shepherd.log(decodedTx, true); + + 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(); + shepherd.log('electrum getbalance ==>', true); + shepherd.log(json, true); + + 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: shepherd.CONNECTION_ERROR_OR_INCOMPLETE_DATA, + }; + + res.end(JSON.stringify(successObj)); + } + }); + }); + return shepherd; +}; \ No newline at end of file diff --git a/routes/shepherd/electrum/block.js b/routes/shepherd/electrum/block.js new file mode 100644 index 0000000..3a92911 --- /dev/null +++ b/routes/shepherd/electrum/block.js @@ -0,0 +1,59 @@ +module.exports = (shepherd) => { + shepherd.get('/electrum/getblockinfo', (req, res, next) => { + shepherd.electrumGetBlockInfo(req.query.height, req.query.network) + .then((json) => { + const successObj = { + msg: 'success', + result: json, + }; + + res.end(JSON.stringify(successObj)); + }); + }); + + shepherd.electrumGetBlockInfo = (height, network) => { + return new shepherd.Promise((resolve, reject) => { + const ecl = new shepherd.electrumJSCore(shepherd.electrumServers[network].port, shepherd.electrumServers[network].address, shepherd.electrumServers[network].proto); // tcp or tls + + ecl.connect(); + ecl.blockchainBlockGetHeader(height) + .then((json) => { + ecl.close(); + shepherd.log('electrum getblockinfo ==>', true); + shepherd.log(json, true); + + resolve(json); + }); + }); + } + + shepherd.get('/electrum/getcurrentblock', (req, res, next) => { + shepherd.electrumGetCurrentBlock(req.query.network) + .then((json) => { + const successObj = { + msg: 'success', + result: json, + }; + + res.end(JSON.stringify(successObj)); + }); + }); + + shepherd.electrumGetCurrentBlock = (network) => { + return new shepherd.Promise((resolve, reject) => { + const ecl = new shepherd.electrumJSCore(shepherd.electrumServers[network].port, shepherd.electrumServers[network].address, shepherd.electrumServers[network].proto); // tcp or tls + + ecl.connect(); + ecl.blockchainNumblocksSubscribe() + .then((json) => { + ecl.close(); + shepherd.log('electrum currentblock ==>', true); + shepherd.log(json, true); + + resolve(json); + }); + }); + } + + return shepherd; +}; \ No newline at end of file diff --git a/routes/shepherd/electrum/coins.js b/routes/shepherd/electrum/coins.js new file mode 100644 index 0000000..7fb30a0 --- /dev/null +++ b/routes/shepherd/electrum/coins.js @@ -0,0 +1,69 @@ +module.exports = (shepherd) => { + shepherd.findCoinName = (network) => { + for (let key in shepherd.electrumServers) { + if (key === network) { + return shepherd.electrumServers[key].abbr; + } + } + } + + shepherd.addElectrumCoin = (coin) => { + for (let key in shepherd.electrumServers) { + if (shepherd.electrumServers[key].abbr === coin) { + shepherd.electrumCoins[coin] = { + name: key, + abbr: coin, + server: { + ip: shepherd.electrumServers[key].address, + port: shepherd.electrumServers[key].port, + }, + serverList: shepherd.electrumServers[key].serverList ? shepherd.electrumServers[key].serverList : 'none', + txfee: 'calculated' /*shepherd.electrumServers[key].txfee*/, + }; + + return true; + } + } + } + + shepherd.get('/electrum/coins/remove', (req, res, next) => { + delete shepherd.electrumCoins[req.query.coin]; + + const successObj = { + msg: 'success', + result, + }; + + res.end(JSON.stringify(successObj)); + }); + + shepherd.get('/electrum/coins/add', (req, res, next) => { + const result = shepherd.addElectrumCoin(req.query.coin); + + const successObj = { + msg: 'success', + result, + }; + + res.end(JSON.stringify(successObj)); + }); + + shepherd.get('/electrum/coins', (req, res, next) => { + let _electrumCoins = JSON.parse(JSON.stringify(shepherd.electrumCoins)); // deep cloning + + for (let key in _electrumCoins) { + if (shepherd.electrumKeys[key]) { + _electrumCoins[key].pub = shepherd.electrumKeys[key].pub; + } + } + + const successObj = { + msg: 'success', + result: _electrumCoins, + }; + + res.end(JSON.stringify(successObj)); + }); + + return shepherd; +}; \ No newline at end of file diff --git a/routes/shepherd/electrum/createtx.js b/routes/shepherd/electrum/createtx.js new file mode 100644 index 0000000..f05d43e --- /dev/null +++ b/routes/shepherd/electrum/createtx.js @@ -0,0 +1,411 @@ +module.exports = (shepherd) => { + // single sig + shepherd.buildSignedTx = (sendTo, changeAddress, wif, network, utxo, changeValue, spendValue) => { + let key = shepherd.bitcoinJS.ECPair.fromWIF(wif, shepherd.getNetworkData(network)); + let tx = new shepherd.bitcoinJS.TransactionBuilder(shepherd.getNetworkData(network)); + + shepherd.log('buildSignedTx'); + // console.log(`buildSignedTx priv key ${wif}`); + shepherd.log(`buildSignedTx pub key ${key.getAddress().toString()}`, true); + // console.log('buildSignedTx std tx fee ' + shepherd.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); + shepherd.log(`kmd tx locktime set to ${_locktime}`, true); + } + + shepherd.log('buildSignedTx unsigned tx data vin', true); + shepherd.log(tx.tx.ins, true); + shepherd.log('buildSignedTx unsigned tx data vout', true); + shepherd.log(tx.tx.outs, true); + shepherd.log('buildSignedTx unsigned tx data', true); + shepherd.log(tx, true); + + for (let i = 0; i < utxo.length; i++) { + tx.sign(i, key); + } + + const rawtx = tx.build().toHex(); + + shepherd.log('buildSignedTx signed tx hex', true); + shepherd.log(rawtx, true); + + return rawtx; + } + + shepherd.maxSpendBalance = (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', (req, res, next) => { + // txid 64 char + const network = req.query.network || shepherd.findNetworkObj(req.query.coin); + const ecl = new shepherd.electrumJSCore(shepherd.electrumServers[network].port, shepherd.electrumServers[network].address, shepherd.electrumServers[network].proto); // tcp or tls + const outputAddress = req.query.address; + const changeAddress = req.query.change; + const value = Number(req.query.value); + const push = req.query.push; + const fee = shepherd.electrumServers[network].txfee; + let wif = req.query.wif; + + if (req.query.gui) { + wif = shepherd.electrumKeys[req.query.coin].priv; + } + + shepherd.log('electrum createrawtx =>', true); + + 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, + }); + } + } + + shepherd.log('electrum listunspent unformatted ==>', true); + shepherd.log(utxoList, true); + + shepherd.log('electrum listunspent formatted ==>', true); + shepherd.log(utxoListFormatted, true); + + const _maxSpendBalance = Number(shepherd.maxSpendBalance(utxoListFormatted)); + let targets = [{ + address: outputAddress, + value: value > _maxSpendBalance ? _maxSpendBalance : value, + }]; + shepherd.log('targets =>', true); + shepherd.log(targets, true); + + 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 } = shepherd.coinSelect(utxoListFormatted, targets, feeRate); + + shepherd.log('coinselect res =>', true); + shepherd.log('coinselect inputs =>', true); + shepherd.log(inputs, true); + shepherd.log('coinselect outputs =>', true); + shepherd.log(outputs, true); + shepherd.log('coinselect calculated fee =>', true); + shepherd.log(fee, true); + + if (!inputs && + !outputs) { + targets[0].value = targets[0].value - shepherd.electrumServers[network].txfee; + shepherd.log('second run', true); + shepherd.log('coinselect adjusted targets =>', true); + shepherd.log(targets, true); + + const secondRun = shepherd.coinSelect(utxoListFormatted, targets, feeRate); + inputs = secondRun.inputs; + outputs = secondRun.outputs; + fee = secondRun.fee; + + shepherd.log('coinselect inputs =>', true); + shepherd.log(inputs, true); + shepherd.log('coinselect outputs =>', true); + shepherd.log(outputs, true); + shepherd.log('coinselect fee =>', true); + shepherd.log(fee, true); + } + + 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 { + shepherd.log(`maxspend ${_maxSpend} (${_maxSpend * 0.00000001})`, true); + shepherd.log(`value ${value}`, true); + shepherd.log(`sendto ${outputAddress} amount ${value} (${value * 0.00000001})`, true); + shepherd.log(`changeto ${changeAddress} amount ${_change} (${_change * 0.00000001})`, true); + + // account for KMD interest + if (network === 'komodo' && + totalInterest > 0) { + // account for extra vout + const _feeOverhead = outputs.length === 1 ? shepherd.estimateTxSize(0, 1) * feeRate : 0; + + shepherd.log(`max interest to claim ${totalInterest} (${totalInterest * 0.00000001})`, true); + shepherd.log(`estimated fee overhead ${_feeOverhead}`, true); + shepherd.log(`current change amount ${_change} (${_change * 0.00000001}), boosted change amount ${_change + (totalInterest - _feeOverhead)} (${(_change + (totalInterest - _feeOverhead)) * 0.00000001})`, true); + + _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; + } + + const _estimatedFee = vinSum - outputs[0].value - _change; + + shepherd.log(`vin sum ${vinSum} (${vinSum * 0.00000001})`, true); + shepherd.log(`estimatedFee ${_estimatedFee} (${_estimatedFee * 0.00000001})`, true); + + 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 shepherd.electrumJSCore(shepherd.electrumServers[network].port, shepherd.electrumServers[network].address, shepherd.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', (req, res, next) => { + const rawtx = req.query.rawtx; + const ecl = new shepherd.electrumJSCore(shepherd.electrumServers[req.query.network].port, shepherd.electrumServers[req.query.network].address, shepherd.electrumServers[req.query.network].proto); // tcp or tls + + ecl.connect(); + ecl.blockchainTransactionBroadcast(rawtx) + .then((json) => { + ecl.close(); + shepherd.log('electrum pushtx ==>', true); + shepherd.log(json, true); + + const successObj = { + msg: 'success', + result: json, + }; + + res.end(JSON.stringify(successObj)); + }); + }); + + return shepherd; +}; \ No newline at end of file diff --git a/routes/shepherd/electrum/estimate.js b/routes/shepherd/electrum/estimate.js new file mode 100644 index 0000000..fab2b98 --- /dev/null +++ b/routes/shepherd/electrum/estimate.js @@ -0,0 +1,26 @@ +module.exports = (shepherd) => { + shepherd.get('/electrum/estimatefee', (req, res, next) => { + const ecl = new shepherd.electrumJSCore(shepherd.electrumServers[req.query.network].port, shepherd.electrumServers[req.query.network].address, shepherd.electrumServers[req.query.network].proto); // tcp or tls + + ecl.connect(); + ecl.blockchainEstimatefee(req.query.blocks) + .then((json) => { + ecl.close(); + shepherd.log('electrum estimatefee ==>', true); + + const successObj = { + msg: 'success', + result: json, + }; + + res.end(JSON.stringify(successObj)); + }); + }); + + shepherd.estimateTxSize = (numVins, numOuts) => { + // in x 180 + out x 34 + 10 plus or minus in + return numVins * 180 + numOuts * 34 + 11; + } + + return shepherd; +}; \ No newline at end of file diff --git a/routes/shepherd/electrum/interest.js b/routes/shepherd/electrum/interest.js new file mode 100644 index 0000000..8a954e7 --- /dev/null +++ b/routes/shepherd/electrum/interest.js @@ -0,0 +1,37 @@ +module.exports = (shepherd) => { + shepherd.kmdCalcInterest = (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; + + shepherd.log('kmdCalcInterest', true); + shepherd.log(`locktime ${locktime}`, true); + shepherd.log(`minutes converted ${timestampDiffMinutes}`, true); + shepherd.log(`passed ${hoursPassed}h ${minutesPassed}m ${secondsPassed}s`, true); + + // calc interest + if (timestampDiffMinutes >= 60) { + if (timestampDiffMinutes > 365 * 24 * 60) { + timestampDiffMinutes = 365 * 24 * 60; + } + timestampDiffMinutes -= 59; + + shepherd.log(`minutes if statement ${timestampDiffMinutes}`, true); + + // 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; + shepherd.log(`interest ${interest}`, true); + } + + return interest; + } + + return shepherd; +}; \ No newline at end of file diff --git a/routes/shepherd/electrum/keys.js b/routes/shepherd/electrum/keys.js new file mode 100644 index 0000000..8a847d1 --- /dev/null +++ b/routes/shepherd/electrum/keys.js @@ -0,0 +1,128 @@ +module.exports = (shepherd) => { + shepherd.seedToWif = (seed, network, iguana) => { + const bytes = shepherd.sha256(seed, { asBytes: true }); + + if (iguana) { + bytes[0] &= 248; + bytes[31] &= 127; + bytes[31] |= 64; + } + + const toHexString = (byteArray) => { + return Array.from(byteArray, (byte) => { + return ('0' + (byte & 0xFF).toString(16)).slice(-2); + }).join(''); + } + + const hex = toHexString(bytes); + + const key = new shepherd.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}`, true); + shepherd.log(`seedtowif pub key ${key.publicAddress}`, true); + + return { + priv: key.privateWif, + pub: key.publicAddress, + }; + } + + shepherd.get('/electrum/seedtowif', (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.get('/electrum/keys', (req, res, next) => { + let _matchingKeyPairs = 0; + let _electrumKeys = {}; + + for (let key in shepherd.electrumServers) { + const _abbr = shepherd.electrumServers[key].abbr; + const { priv, pub } = shepherd.seedToWif(req.query.seed, shepherd.findNetworkObj(_abbr), req.query.iguana); + + if (shepherd.electrumKeys[_abbr].pub === pub && + shepherd.electrumKeys[_abbr].priv === priv) { + _matchingKeyPairs++; + } + } + + if (req.query.active) { + _electrumKeys = JSON.parse(JSON.stringify(shepherd.electrumKeys)); + + for (let key in _electrumKeys) { + if (!shepherd.electrumCoins[key]) { + delete _electrumKeys[key]; + } + } + } else { + _electrumKeys = shepherd.electrumKeys; + } + + shepherd.log(JSON.stringify(_electrumKeys, null, '\t'), true); + + const successObj = { + msg: 'success', + result: _matchingKeyPairs === Object.keys(shepherd.electrumKeys).length ? _electrumKeys : false, + }; + + res.end(JSON.stringify(successObj)); + }); + + // spv v2 + /*shepherd.get('/electrum/bip39/seed', (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: shepherd.electrumServers, + }, + }; + + res.end(JSON.stringify(successObj)); + + console.log(shepherd.bitcoinJS.networks.komodo); + const hdMaster = shepherd.bitcoinJS.HDNode.fromSeedBuffer(seed, shepherd.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 = shepherd.bitcoinJS.HDNode.fromSeedBuffer(seed, shepherd.electrumJSNetworks.komodo).deriveHardened(0).derive(0).derive(1); + console.log(`address: ${hdnode.getAddress()}`); + console.log(`priv (WIF): ${hdnode.keyPair.toWIF()}`); + });*/ + + return shepherd; +}; \ No newline at end of file diff --git a/routes/shepherd/electrum/listunspent.js b/routes/shepherd/electrum/listunspent.js new file mode 100644 index 0000000..7db5f50 --- /dev/null +++ b/routes/shepherd/electrum/listunspent.js @@ -0,0 +1,194 @@ +module.exports = (shepherd) => { + shepherd.listunspent = (ecl, address, network, full, verify) => { + let _atLeastOneDecodeTxFailed = false; + + if (full) { + return new shepherd.Promise((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 { + shepherd.Promise.all(_utxo.map((_utxoItem, index) => { + return new shepherd.Promise((resolve, reject) => { + ecl.blockchainTransactionGet(_utxoItem['tx_hash']) + .then((_rawtxJSON) => { + shepherd.log('electrum gettransaction ==>', true); + shepherd.log(index + ' | ' + (_rawtxJSON.length - 1), true); + shepherd.log(_rawtxJSON, true); + + // decode tx + const _network = shepherd.getNetworkData(network); + const decodedTx = shepherd.electrumJSTxDecoder(_rawtxJSON, _network); + + shepherd.log('decoded tx =>', true); + shepherd.log(decodedTx, true); + + 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.kmdCalcInterest(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 === shepherd.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 === shepherd.CONNECTION_ERROR_OR_INCOMPLETE_DATA) { + verifyMerkleRes = false; + } + + _resolveObj.verified = verifyMerkleRes; + resolve(_resolveObj); + }); + } else { + resolve(_resolveObj); + } + } + } + }); + }); + })) + .then(promiseResult => { + ecl.close(); + + if (!_atLeastOneDecodeTxFailed) { + shepherd.log(promiseResult, true); + resolve(promiseResult); + } else { + shepherd.log('listunspent error, cant decode tx(s)', true); + resolve('decode error'); + } + }); + } + } else { + resolve('cant get current height'); + } + }); + } else { + ecl.close(); + resolve(shepherd.CONNECTION_ERROR_OR_INCOMPLETE_DATA); + } + }); + }); + } else { + return new shepherd.Promise((resolve, reject) => { + ecl.connect(); + ecl.blockchainAddressListunspent(address) + .then((json) => { + ecl.close(); + + if (json && + json.length) { + resolve(json); + } else { + resolve(shepherd.CONNECTION_ERROR_OR_INCOMPLETE_DATA); + } + }); + }); + } + } + + shepherd.get('/electrum/listunspent', (req, res, next) => { + const network = req.query.network || shepherd.findNetworkObj(req.query.coin); + const ecl = new shepherd.electrumJSCore(shepherd.electrumServers[network].port, shepherd.electrumServers[network].address, shepherd.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) => { + shepherd.log('electrum listunspent ==>', true); + + const successObj = { + msg: 'success', + result: listunspent, + }; + + res.end(JSON.stringify(successObj)); + }); + } else { + shepherd.listunspent(ecl, req.query.address, network) + .then((listunspent) => { + ecl.close(); + shepherd.log('electrum listunspent ==>', true); + + const successObj = { + msg: 'success', + result: listunspent, + }; + + res.end(JSON.stringify(successObj)); + }); + } + }); + + return shepherd; +}; \ No newline at end of file diff --git a/routes/shepherd/electrum/merkle.js b/routes/shepherd/electrum/merkle.js new file mode 100644 index 0000000..b3fbe03 --- /dev/null +++ b/routes/shepherd/electrum/merkle.js @@ -0,0 +1,145 @@ +module.exports = (shepherd) => { + // get merkle root + shepherd.getMerkleRoot = (txid, proof, pos) => { + const reverse = require('buffer-reverse'); + let hash = txid; + let serialized; + const _sha256 = (data) => { + return shepherd.crypto.createHash('sha256').update(data).digest(); + } + + shepherd.log(`getMerkleRoot txid ${txid}`, true); + shepherd.log(`getMerkleRoot pos ${pos}`, true); + shepherd.log('getMerkleRoot proof', true); + shepherd.log(`getMerkleRoot ${proof}`, true); + + 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 = (txid, height, serverList, mainServer) => { + // select random server + const getRandomIntInclusive = (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 shepherd.electrumJSCore(_mainServer[1], _mainServer[0], 'tcp'); // tcp or tls + + return new shepherd.Promise((resolve, reject) => { + shepherd.log(`main server: ${mainServer}`, true); + shepherd.log(`verification server: ${randomServer}`, true); + + ecl.connect(); + ecl.blockchainTransactionGetMerkle(txid, height) + .then((merkleData) => { + if (merkleData && + merkleData.merkle && + merkleData.pos) { + shepherd.log('electrum getmerkle =>', true); + shepherd.log(merkleData, true); + ecl.close(); + + const _res = shepherd.getMerkleRoot(txid, merkleData.merkle, merkleData.pos); + shepherd.log(_res, true); + + ecl = new shepherd.electrumJSCore(_randomServer[1], _randomServer[0], 'tcp'); + ecl.connect(); + + ecl.blockchainBlockGetHeader(height) + .then((blockInfo) => { + if (blockInfo && + blockInfo['merkle_root']) { + ecl.close(); + shepherd.log('blockinfo =>', true); + shepherd.log(blockInfo, true); + shepherd.log(blockInfo['merkle_root'], true); + + if (blockInfo && + blockInfo['merkle_root']) { + if (_res === blockInfo['merkle_root']) { + resolve(true); + } else { + resolve(false); + } + } else { + resolve(shepherd.CONNECTION_ERROR_OR_INCOMPLETE_DATA); + } + } else { + resolve(shepherd.CONNECTION_ERROR_OR_INCOMPLETE_DATA); + } + }); + } else { + resolve(shepherd.CONNECTION_ERROR_OR_INCOMPLETE_DATA); + } + }); + }); + } + + shepherd.verifyMerkleByCoin = (coin, txid, height) => { + const _serverList = shepherd.electrumCoins[coin].serverList; + + shepherd.log(`verifyMerkleByCoin`, true); + shepherd.log(shepherd.electrumCoins[coin].server, true); + shepherd.log(shepherd.electrumCoins[coin].serverList, true); + + return new shepherd.Promise((resolve, reject) => { + if (_serverList !== 'none') { + let _filteredServerList = []; + + for (let i = 0; i < _serverList.length; i++) { + if (_serverList[i] !== shepherd.electrumCoins[coin].server.ip + ':' + shepherd.electrumCoins[coin].server.port) { + _filteredServerList.push(_serverList[i]); + } + } + + shepherd.verifyMerkle( + txid, + height, + _filteredServerList, + shepherd.electrumCoins[coin].server.ip + ':' + shepherd.electrumCoins[coin].server.port + ).then((proof) => { + resolve(proof); + }); + } else { + resolve(false); + } + }); + } + + shepherd.get('/electrum/merkle/verify', (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)); + }); + }); + + return shepherd; +}; diff --git a/routes/shepherd/electrum/network.js b/routes/shepherd/electrum/network.js new file mode 100644 index 0000000..0cf81db --- /dev/null +++ b/routes/shepherd/electrum/network.js @@ -0,0 +1,141 @@ +module.exports = (shepherd) => { + shepherd.getNetworkData = (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 shepherd.electrumJSNetworks.komodo; + } else { + return shepherd.electrumJSNetworks[network]; + } + } + + shepherd.findNetworkObj = (coin) => { + for (let key in shepherd.electrumServers) { + if (shepherd.electrumServers[key].abbr === coin) { + return key; + } + } + } + + shepherd.get('/electrum/servers', (req, res, next) => { + if (req.query.abbr) { + let _electrumServers = {}; + + for (let key in shepherd.electrumServers) { + _electrumServers[shepherd.electrumServers[key].abbr] = shepherd.electrumServers[key]; + } + + const successObj = { + msg: 'success', + result: { + servers: _electrumServers, + }, + }; + + res.end(JSON.stringify(successObj)); + } else { + const successObj = { + msg: 'success', + result: { + servers: shepherd.electrumServers, + }, + }; + + res.end(JSON.stringify(successObj)); + } + }); + + shepherd.get('/electrum/coins/server/set', (req, res, next) => { + shepherd.electrumCoins[req.query.coin].server = { + ip: req.query.address, + port: req.query.port, + }; + + for (let key in shepherd.electrumServers) { + if (shepherd.electrumServers[key].abbr === req.query.coin) { // a bit risky + shepherd.electrumServers[key].address = req.query.address; + shepherd.electrumServers[key].port = req.query.port; + break; + } + } + + shepherd.log(JSON.stringify(shepherd.electrumCoins[req.query.coin], null, '\t'), true); + + const successObj = { + msg: 'success', + result: true, + }; + + res.end(JSON.stringify(successObj)); + }); + + shepherd.get('/electrum/servers/test', (req, res, next) => { + const ecl = new shepherd.electrumJSCore(req.query.port, req.query.address, 'tcp'); // tcp or tls + + ecl.connect(); + ecl.serverVersion() + .then((serverData) => { + ecl.close(); + shepherd.log('serverData', true); + shepherd.log(serverData, true); + + 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)); + } + }); + }); + + return shepherd; +}; \ No newline at end of file diff --git a/routes/shepherd/electrum/transactions.js b/routes/shepherd/electrum/transactions.js new file mode 100644 index 0000000..d49d58f --- /dev/null +++ b/routes/shepherd/electrum/transactions.js @@ -0,0 +1,397 @@ +module.exports = (shepherd) => { + shepherd.sortTransactions = (transactions) => { + return transactions.sort((b, a) => { + if (a.height < b.height) { + return -1; + } + + if (a.height > b.height) { + return 1; + } + + return 0; + }); + } + + shepherd.get('/electrum/listtransactions', (req, res, next) => { + const network = req.query.network || shepherd.findNetworkObj(req.query.coin); + const ecl = new shepherd.electrumJSCore(shepherd.electrumServers[network].port, shepherd.electrumServers[network].address, shepherd.electrumServers[network].proto); // tcp or tls + + shepherd.log('electrum listtransactions ==>', true); + + if (!req.query.full) { + ecl.connect(); + ecl.blockchainAddressGetHistory(req.query.address) + .then((json) => { + ecl.close(); + shepherd.log(json, true); + + json = shepherd.sortTransactions(json); + + const successObj = { + msg: 'success', + result: 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); + let _rawtx = []; + + shepherd.log(json.length, true); + + shepherd.Promise.all(json.map((transaction, index) => { + return new shepherd.Promise((resolve, reject) => { + ecl.blockchainBlockGetHeader(transaction.height) + .then((blockInfo) => { + if (blockInfo && + blockInfo.timestamp) { + ecl.blockchainTransactionGet(transaction['tx_hash']) + .then((_rawtxJSON) => { + shepherd.log('electrum gettransaction ==>', true); + shepherd.log((index + ' | ' + (_rawtxJSON.length - 1)), true); + shepherd.log(_rawtxJSON, true); + + // decode tx + const _network = shepherd.getNetworkData(network); + const decodedTx = shepherd.electrumJSTxDecoder(_rawtxJSON, _network); + + let txInputs = []; + + shepherd.log('decodedtx =>', true); + shepherd.log(decodedTx.outputs, true); + + if (decodedTx && + decodedTx.inputs) { + shepherd.Promise.all(decodedTx.inputs.map((_decodedInput, index) => { + return new shepherd.Promise((_resolve, _reject) => { + if (_decodedInput.txid !== '0000000000000000000000000000000000000000000000000000000000000000') { + ecl.blockchainTransactionGet(_decodedInput.txid) + .then((rawInput) => { + const decodedVinVout = shepherd.electrumJSTxDecoder(rawInput, _network); + + shepherd.log('electrum raw input tx ==>', true); + + if (decodedVinVout) { + shepherd.log(decodedVinVout.outputs[_decodedInput.n], true); + 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) { + formattedTx.height = transaction.height; + formattedTx.blocktime = blockInfo.timestamp; + formattedTx.timereceived = blockInfo.timereceived; + formattedTx.hex = _rawtxJSON; + formattedTx.inputs = decodedTx.inputs; + formattedTx.outputs = decodedTx.outputs; + formattedTx.locktime = decodedTx.format.locktime; + _rawtx.push(formattedTx); + } else { + formattedTx[0].height = transaction.height; + formattedTx[0].blocktime = blockInfo.timestamp; + formattedTx[0].timereceived = blockInfo.timereceived; + formattedTx[0].hex = _rawtxJSON; + formattedTx[0].inputs = decodedTx.inputs; + formattedTx[0].outputs = decodedTx.outputs; + formattedTx[0].locktime = decodedTx.format.locktime; + formattedTx[1].height = transaction.height; + formattedTx[1].blocktime = blockInfo.timestamp; + formattedTx[1].timereceived = blockInfo.timereceived; + formattedTx[1].hex = _rawtxJSON; + formattedTx[1].inputs = decodedTx.inputs; + formattedTx[1].outputs = decodedTx.outputs; + formattedTx[1].locktime = decodedTx.format.locktime; + _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', (req, res, next) => { + const network = req.query.network || shepherd.findNetworkObj(req.query.coin); + const ecl = new shepherd.electrumJSCore(shepherd.electrumServers[network].port, shepherd.electrumServers[network].address, shepherd.electrumServers[network].proto); // tcp or tls + + shepherd.log('electrum gettransaction =>', true); + + ecl.connect(); + ecl.blockchainTransactionGet(req.query.txid) + .then((json) => { + ecl.close(); + shepherd.log(json, true); + + const successObj = { + msg: 'success', + result: json, + }; + + res.end(JSON.stringify(successObj)); + }); + }); + + shepherd.parseTransactionAddresses = (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, + }; + + shepherd.log('parseTransactionAddresses result ==>', true); + + 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++) { + shepherd.log(`key ==>`, true); + shepherd.log(_parse[key][i], true); + shepherd.log(Number(_parse[key][i].value), true); + + _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, + }; + } + + shepherd.log(_sum, true); + shepherd.log(result, true); + + return result; + } + + shepherd.get('/electrum/decoderawtx', (req, res, next) => { + const _network = shepherd.getNetworkData(req.query.network); + const _rawtx = req.query.rawtx; + // const _rawtx = '0100000001dd6d064f5665f8454293ecaa9dbb55accf4f7e443d35f3b5ab7760f54b6c15fe000000006a473044022056355585a4a501ec9afc96aa5df124cf29ad3ac6454b47cd07cd7d89ec95ec2b022074c4604ee349d30e5336f210598e4dc576bf16ebeb67eeac3f4e82f56e930fee012103b90ba01af308757054e0484bb578765d5df59c4a57adbb94e2419df5e7232a63feffffff0289fc923b000000001976a91424af38fcb13bbc171b0b42bb017244a53b6bb2fa88ac20a10700000000001976a9142f4c0f91fc06ac228c120aee41741d0d3909683288ac49258b58'; + const decodedTx = shepherd.electrumJSTxDecoder(_rawtx, _network); + + shepherd.log('electrum decoderawtx input tx ==>', true); + + if (req.query.parseonly || + decodedTx.inputs[0].txid === '0000000000000000000000000000000000000000000000000000000000000000') { + const successObj = { + msg: 'success', + result: { + network: decodedTx.network, + format: decodedTx.format, + inputs: decodedTx.inputs, + outputs: decodedTx.outputs, + }, + }; + + shepherd.log(successObj.result, true); + + res.end(JSON.stringify(successObj)); + } else { + const ecl = new shepherd.electrumJSCore(shepherd.electrumServers[req.query.network].port, shepherd.electrumServers[req.query.network].address, shepherd.electrumServers[req.query.network].proto); // tcp or tls + + ecl.connect(); + ecl.blockchainTransactionGet(decodedTx.inputs[0].txid) + .then((json) => { + ecl.close(); + shepherd.log(json, true); + + const decodedVin = shepherd.electrumJSTxDecoder(json, _network); + + const successObj = { + msg: 'success', + result: { + network: decodedTx.network, + format: decodedTx.format, + inputs: decodedVin.outputs[decodedTx.inputs[0].n], + outputs: decodedTx.outputs, + }, + }; + + res.end(JSON.stringify(successObj)); + }); + } + }); + + return shepherd; +}; \ No newline at end of file diff --git a/routes/shepherd/init.js b/routes/shepherd/init.js new file mode 100644 index 0000000..d4a183b --- /dev/null +++ b/routes/shepherd/init.js @@ -0,0 +1,52 @@ +module.exports = (shepherd) => { + shepherd.readVersionFile = () => { + // read app version + const rootLocation = shepherd.path.join(__dirname, '../../'); + const localVersionFile = shepherd.fs.readFileSync(`${rootLocation}version`, 'utf8'); + + return localVersionFile; + } + + shepherd.createAgamaDirs = () => { + if (!shepherd.fs.existsSync(shepherd.agamaDir)) { + shepherd.fs.mkdirSync(shepherd.agamaDir); + + if (shepherd.fs.existsSync(shepherd.agamaDir)) { + shepherd.log(`created agama folder at ${shepherd.agamaDir}`); + shepherd.writeLog(`created agama folder at ${shepherd.agamaDir}`); + } + } else { + shepherd.log('agama folder already exists'); + } + + if (!shepherd.fs.existsSync(`${shepherd.agamaDir}/shepherd`)) { + shepherd.fs.mkdirSync(`${shepherd.agamaDir}/shepherd`); + + if (shepherd.fs.existsSync(`${shepherd.agamaDir}/shepherd`)) { + shepherd.log(`created shepherd folder at ${shepherd.agamaDir}/shepherd`); + shepherd.writeLog(`create shepherd folder at ${shepherd.agamaDir}/shepherd`); + } + } else { + shepherd.log('agama/shepherd folder already exists'); + } + + if (!shepherd.fs.existsSync(`${shepherd.agamaDir}/shepherd/pin`)) { + shepherd.fs.mkdirSync(`${shepherd.agamaDir}/shepherd/pin`); + + if (shepherd.fs.existsSync(`${shepherd.agamaDir}/shepherd/pin`)) { + shepherd.log(`created pin folder at ${shepherd.agamaDir}/shepherd/pin`); + shepherd.writeLog(`create pin folder at ${shepherd.agamaDir}/shepherd/pin`); + } + } else { + shepherd.log('shepherd/pin folder already exists'); + } + + if (!shepherd.fs.existsSync(shepherd.zcashParamsDir)) { + shepherd.fs.mkdirSync(shepherd.zcashParamsDir); + } else { + shepherd.log('zcashparams folder already exists'); + } + } + + return shepherd; +}; \ No newline at end of file diff --git a/routes/shepherd/kickstart.js b/routes/shepherd/kickstart.js new file mode 100644 index 0000000..cbe7c09 --- /dev/null +++ b/routes/shepherd/kickstart.js @@ -0,0 +1,150 @@ +module.exports = (shepherd) => { + /* + * type: GET + * params: coin, type + * TODO: reorganize to work with coind + */ + shepherd.get('/kick', (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 = shepherd.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)); + } + }); + + return shepherd; +}; \ No newline at end of file diff --git a/routes/shepherd/log.js b/routes/shepherd/log.js new file mode 100644 index 0000000..9bf8472 --- /dev/null +++ b/routes/shepherd/log.js @@ -0,0 +1,141 @@ +module.exports = (shepherd) => { + shepherd.log = (msg, isSpvOut) => { + if (shepherd.appConfig.dev || + shepherd.appConfig.debug) { + console.log(msg); + } + + if (!isSpvOut) { + shepherd.appRuntimeLog.push({ + time: Date.now(), + msg: msg, + }); + } else { + shepherd.appRuntimeSPVLog.push({ + time: Date.now(), + msg: msg, + }); + } + } + + shepherd.writeLog = (data) => { + const logLocation = `${shepherd.agamaDir}/shepherd`; + const timeFormatted = new Date(Date.now()).toLocaleString('en-US', { hour12: false }); + + if (shepherd.appConfig.debug) { + if (shepherd.fs.existsSync(`${logLocation}/agamalog.txt`)) { + shepherd.fs.appendFile(`${logLocation}/agamalog.txt`, `${timeFormatted} ${data}\r\n`, (err) => { + if (err) { + shepherd.log('error writing log file'); + } + }); + } else { + shepherd.fs.writeFile(`${logLocation}/agamalog.txt`, `${timeFormatted} ${data}\r\n`, (err) => { + if (err) { + shepherd.log('error writing log file'); + } + }); + } + } + } + + shepherd.get('/log/runtime', (req, res, next) => { + const successObj = { + msg: 'success', + result: req.query.spv && req.query.spv === 'true' ? shepherd.appRuntimeSPVLog : shepherd.appRuntimeLog, + }; + + res.end(JSON.stringify(successObj)); + }); + + shepherd.getAppRuntimeLog = () => { + return new shepherd.Promise((resolve, reject) => { + resolve(shepherd.appRuntimeLog); + }); + }; + + /* + * type: POST + * params: payload + */ + shepherd.post('/guilog', (req, res, next) => { + const logLocation = `${shepherd.agamaDir}/shepherd`; + + if (!shepherd.guiLog[shepherd.appSessionHash]) { + shepherd.guiLog[shepherd.appSessionHash] = {}; + } + + if (shepherd.guiLog[shepherd.appSessionHash][req.body.timestamp]) { + shepherd.guiLog[shepherd.appSessionHash][req.body.timestamp].status = req.body.status; + shepherd.guiLog[shepherd.appSessionHash][req.body.timestamp].response = req.body.response; + } else { + shepherd.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, + }; + } + + shepherd.fs.writeFile(`${logLocation}/agamalog.json`, JSON.stringify(shepherd.guiLog), (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', (req, res, next) => { + const logExt = req.query.type === 'txt' ? 'txt' : 'json'; + + if (shepherd.fs.existsSync(`${shepherd.agamaDir}/shepherd/agamalog.${logExt}`)) { + shepherd.fs.readFile(`${shepherd.agamaDir}/shepherd/agamalog.${logExt}`, 'utf8', (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)); + } + }); + + shepherd.printDirs = () => { + shepherd.log(`agama dir: ${shepherd.agamaDir}`); + shepherd.log('--------------------------') + shepherd.log(`komodo dir: ${shepherd.komododBin}`); + shepherd.log(`komodo bin: ${shepherd.komodoDir}`); + shepherd.writeLog(`agama dir: ${shepherd.agamaDir}`); + shepherd.writeLog(`komodo dir: ${shepherd.komododBin}`); + shepherd.writeLog(`komodo bin: ${shepherd.komodoDir}`); + } + + return shepherd; +}; \ No newline at end of file diff --git a/routes/shepherd/paths.js b/routes/shepherd/paths.js new file mode 100644 index 0000000..3f15995 --- /dev/null +++ b/routes/shepherd/paths.js @@ -0,0 +1,74 @@ +module.exports = (shepherd) => { + shepherd.pathsAgama = () => { + switch (shepherd.os.platform()) { + case 'darwin': + shepherd.fixPath(); + shepherd.agamaDir = `${process.env.HOME}/Library/Application Support/Agama`; + break; + + case 'linux': + shepherd.agamaDir = `${process.env.HOME}/.agama`; + break; + + case 'win32': + shepherd.agamaDir = `${process.env.APPDATA}/Agama`; + shepherd.agamaDir = shepherd.path.normalize(shepherd.agamaDir); + break; + } + } + + shepherd.pathsDaemons = () => { + switch (shepherd.os.platform()) { + case 'darwin': + shepherd.fixPath(); + shepherd.agamaTestDir = `${process.env.HOME}/Library/Application Support/Agama/test`, + shepherd.komododBin = shepherd.path.join(__dirname, '../../assets/bin/osx/komodod'), + shepherd.komodocliBin = shepherd.path.join(__dirname, '../../assets/bin/osx/komodo-cli'), + shepherd.komodoDir = shepherd.appConfig.dataDir.length ? shepherd.appConfig.dataDir : `${process.env.HOME}/Library/Application Support/Komodo`, + shepherd.zcashdBin = '/Applications/ZCashSwingWalletUI.app/Contents/MacOS/zcashd', + shepherd.zcashcliBin = '/Applications/ZCashSwingWalletUI.app/Contents/MacOS/zcash-cli', + shepherd.zcashDir = `${process.env.HOME}/Library/Application Support/Zcash`, + shepherd.zcashParamsDir = `${process.env.HOME}/Library/Application Support/ZcashParams`, + shepherd.chipsBin = shepherd.path.join(__dirname, '../../assets/bin/osx/chipsd'), + shepherd.chipscliBin = shepherd.path.join(__dirname, '../../assets/bin/osx/chips-cli'), + shepherd.chipsDir = `${process.env.HOME}/Library/Application Support/Chips`, + shepherd.coindRootDir = shepherd.path.join(__dirname, '../../assets/bin/osx/dex/coind'); + break; + + case 'linux': + shepherd.agamaTestDir = `${process.env.HOME}/.agama/test`, + shepherd.komododBin = shepherd.path.join(__dirname, '../../assets/bin/linux64/komodod'), + shepherd.komodocliBin = shepherd.path.join(__dirname, '../../assets/bin/linux64/komodo-cli'), + shepherd.komodoDir = shepherd.appConfig.dataDir.length ? shepherd.appConfig.dataDir : `${process.env.HOME}/.komodo`, + shepherd.zcashParamsDir = `${process.env.HOME}/.zcash-params`, + shepherd.chipsBin = shepherd.path.join(__dirname, '../../assets/bin/linux64/chipsd'), + shepherd.chipscliBin = shepherd.path.join(__dirname, '../../assets/bin/linux64/chips-cli'), + shepherd.chipsDir = `${process.env.HOME}/.chips`, + shepherd.coindRootDir = shepherd.path.join(__dirname, '../../assets/bin/linux64/dex/coind'); + break; + + case 'win32': + shepherd.agamaTestDir = `${process.env.APPDATA}/Agama/test`; + shepherd.agamaTestDir = shepherd.path.normalize(shepherd.agamaTestDir); + shepherd.komododBin = shepherd.path.join(__dirname, '../../assets/bin/win64/komodod.exe'), + shepherd.komododBin = shepherd.path.normalize(shepherd.komododBin), + shepherd.komodocliBin = shepherd.path.join(__dirname, '../../assets/bin/win64/komodo-cli.exe'), + shepherd.komodocliBin = shepherd.path.normalize(shepherd.komodocliBin), + shepherd.komodoDir = shepherd.appConfig.dataDir.length ? shepherd.appConfig.dataDir : `${process.env.APPDATA}/Komodo`, + shepherd.komodoDir = shepherd.path.normalize(shepherd.komodoDir); + shepherd.chipsBin = shepherd.path.join(__dirname, '../../assets/bin/win64/chipsd.exe'), + shepherd.chipsBin = shepherd.path.normalize(shepherd.chipsBin), + shepherd.chipscliBin = shepherd.path.join(__dirname, '../../assets/bin/win64/chips-cli.exe'), + shepherd.chipscliBin = shepherd.path.normalize(shepherd.chipscliBin), + shepherd.chipsDir = `${process.env.APPDATA}/Chips`, + shepherd.chipsDir = shepherd.path.normalize(shepherd.chipsDir); + shepherd.zcashParamsDir = `${process.env.APPDATA}/ZcashParams`; + shepherd.zcashParamsDir = shepherd.path.normalize(shepherd.zcashParamsDir); + shepherd.coindRootDir = shepherd.path.join(__dirname, '../../assets/bin/osx/dex/coind'); + shepherd.coindRootDir = shepherd.path.normalize(shepherd.coindRootDir); + break; + } + } + + return shepherd; +}; \ No newline at end of file diff --git a/routes/shepherd/pin.js b/routes/shepherd/pin.js new file mode 100644 index 0000000..20a47c7 --- /dev/null +++ b/routes/shepherd/pin.js @@ -0,0 +1,146 @@ +module.exports = (shepherd) => { + /* + * type: POST + * params: none + */ + shepherd.post('/encryptkey', (req, res, next) => { + if (req.body.key && + req.body.string && + req.body.pubkey) { + const encryptedString = shepherd.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}$'); + + shepherd.fs.writeFile(`${shepherd.agamaDir}/shepherd/pin/${req.body.pubkey}.pin`, encryptedString, (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', (req, res, next) => { + if (req.body.key && + req.body.pubkey) { + if (shepherd.fs.existsSync(`${shepherd.agamaDir}/shepherd/pin/${req.body.pubkey}.pin`)) { + shepherd.fs.readFile(`${shepherd.agamaDir}/shepherd/pin/${req.body.pubkey}.pin`, 'utf8', (err, data) => { + if (err) { + const errorObj = { + msg: 'error', + result: err, + }; + + res.end(JSON.stringify(errorObj)); + } else { + const encryptedKey = shepherd.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', (req, res, next) => { + if (shepherd.fs.existsSync(`${shepherd.agamaDir}/shepherd/pin`)) { + shepherd.fs.readdir(`${shepherd.agamaDir}/shepherd/pin`, (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)); + } + }); + + return shepherd; +}; \ No newline at end of file diff --git a/routes/shepherd/quitDaemon.js b/routes/shepherd/quitDaemon.js new file mode 100644 index 0000000..70acb9b --- /dev/null +++ b/routes/shepherd/quitDaemon.js @@ -0,0 +1,82 @@ +module.exports = (shepherd) => { + shepherd.quitKomodod = (timeout = 100) => { + // if komodod is under heavy load it may not respond to cli stop the first time + // exit komodod gracefully + let coindExitInterval = {}; + shepherd.lockDownAddCoin = true; + + for (let key in shepherd.coindInstanceRegistry) { + const chain = key !== 'komodod' ? key : null; + let _coindQuitCmd = shepherd.komodocliBin; + + // any coind + if (shepherd.nativeCoindList[key.toLowerCase()]) { + _coindQuitCmd = `${shepherd.coindRootDir}/${key.toLowerCase()}/${shepherd.nativeCoindList[key.toLowerCase()].bin.toLowerCase()}-cli`; + } + if (key === 'CHIPS') { + _coindQuitCmd = shepherd.chipscliBin; + } + + const execCliStop = () => { + let _arg = []; + if (chain && !shepherd.nativeCoindList[key.toLowerCase()] && key !== 'CHIPS') { + _arg.push(`-ac_name=${chain}`); + + if (shepherd.appConfig.dataDir.length) { + _arg.push(`-datadir=${shepherd.appConfig.dataDir + (key !== 'komodod' ? '/' + key : '')}`); + } + } else if (key === 'komodod' && shepherd.appConfig.dataDir.length) { + _arg.push(`-datadir=${shepherd.appConfig.dataDir}`); + } + + _arg.push('stop'); + shepherd.execFile(`${_coindQuitCmd}`, _arg, (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 shepherd.coindInstanceRegistry[key]; + clearInterval(coindExitInterval[key]); + } + + // workaround for AGT-65 + const _port = shepherd.assetChainPorts[key]; + setTimeout(() => { + shepherd.portscanner.checkPortStatus(_port, '127.0.0.1', (error, status) => { + // Status is 'open' if currently in use or 'closed' if available + if (status === 'closed') { + delete shepherd.coindInstanceRegistry[key]; + clearInterval(coindExitInterval[key]); + } + }); + }, 100); + + if (error !== null) { + shepherd.log(`exec error: ${error}`); + } + + if (key === 'CHIPS') { + setTimeout(() => { + shepherd.killRogueProcess('chips-cli'); + }, 100); + } else { + setTimeout(() => { + shepherd.killRogueProcess('komodo-cli'); + }, 100); + } + }); + } + + execCliStop(); + coindExitInterval[key] = setInterval(() => { + execCliStop(); + }, timeout); + } + } + + return shepherd; +}; diff --git a/routes/shepherd/rpc.js b/routes/shepherd/rpc.js new file mode 100644 index 0000000..4664a16 --- /dev/null +++ b/routes/shepherd/rpc.js @@ -0,0 +1,246 @@ +module.exports = (shepherd) => { + shepherd.getConf = (chain) => { + let _confLocation = chain === 'komodod' ? `${shepherd.komodoDir}/komodo.conf` : `${shepherd.komodoDir}/${chain}/${chain}.conf`; + _confLocation = chain === 'CHIPS' ? `${shepherd.chipsDir}/chips.conf` : _confLocation; + + // any coind + if (chain) { + if (shepherd.nativeCoindList[chain.toLowerCase()]) { + const _osHome = shepherd.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 (shepherd.fs.existsSync(_confLocation)) { + let _port = shepherd.assetChainPorts[chain]; + const _rpcConf = shepherd.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()]) { + shepherd.rpcConf[chain] = parsedRpcConfig; + } else { + shepherd.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', (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 (!shepherd.rpcConf[_chain]) { + shepherd.getConf(req.body.payload.chain === 'KMD' || !req.body.payload.chain && shepherd.kmdMainPassiveMode ? 'komodod' : req.body.payload.chain); + } + + if (_mode === 'default') { + /*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 + shepherd.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 = shepherd.os.platform === 'win32' ? process.env.APPDATA : process.env.HOME; + let coindDebugLogLocation; + + if (_chain === 'CHIPS') { + coindDebugLogLocation = `${shepherd.chipsDir}/debug.log`; + } else { + coindDebugLogLocation = `${_osHome}/.${shepherd.nativeCoindList[_chain.toLowerCase()].bin.toLowerCase()}/debug.log`; + } + + shepherd.readDebugLog(coindDebugLogLocation, 1) + .then((result) => { + const _obj = { + 'msg': 'success', + 'result': result, + }; + + // shepherd.log('bitcoinrpc debug ====>'); + // console.log(result); + + res.end(JSON.stringify(_obj)); + }, (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, + }; + } + + if (req.body.payload.chain) { + const options = { + url: `http://localhost:${shepherd.rpcConf[req.body.payload.chain].port}`, + method: 'POST', + auth: { + 'user': shepherd.rpcConf[req.body.payload.chain].user, + 'pass': shepherd.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 + shepherd.request(options, (error, response, body) => { + if (response && + response.statusCode && + response.statusCode === 200) { + res.end(body); + } else { + res.end(body); + } + }); + } + } + } else { + let _coindCliBin = shepherd.komodocliBin; + + if (shepherd.nativeCoindList && + _chain && + shepherd.nativeCoindList[_chain.toLowerCase()]) { + _coindCliBin = `${shepherd.coindRootDir}/${_chain.toLowerCase()}/${shepherd.nativeCoindList[_chain.toLowerCase()].bin.toLowerCase()}-cli`; + } + + let _arg = (_chain ? ' -ac_name=' + _chain : '') + ' ' + _cmd + _params; + + if (shepherd.appConfig.dataDir.length) { + _arg = `${_arg} -datadir=${shepherd.appConfig.dataDir + (_chain ? '/' + key : '')}`; + } + + _arg = _arg.trim().split(' '); + shepherd.execFile(_coindCliBin, _arg, (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'); + }); + } + } + }); + + return shepherd; +}; \ No newline at end of file