Browse Source

Merge pull request #1 from KomodoPlatform/v0.25

V0.25
v0.25
siulynot 7 years ago
committed by GitHub
parent
commit
6867763d1b
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      LICENSE
  2. 2
      gui/EasyDEX-GUI
  3. 304
      main.js
  4. 22
      package.json
  5. 126
      private/kmdcli.js
  6. 36
      private/mainmenu.js
  7. 24
      routes/appConfig.js
  8. 289
      routes/electrumjs/electrumServers.js
  9. 173
      routes/electrumjs/electrumjs.networks.js
  10. 120
      routes/electrumjs/electrumjs.txdecoder-2bytes.js
  11. 123
      routes/electrumjs/electrumjs.txdecoder-pos.js
  12. 40
      routes/ports.js
  13. 12
      routes/shepherd.js
  14. 150
      routes/shepherd/addCoinShortcuts.js
  15. 19
      routes/shepherd/appInfo.js
  16. 18
      routes/shepherd/auth.js
  17. 239
      routes/shepherd/binsTestUtil.js
  18. 9
      routes/shepherd/coindWalletKeys.js
  19. 9
      routes/shepherd/coins.js
  20. 18
      routes/shepherd/coinsList.js
  21. 27
      routes/shepherd/config.js
  22. 136
      routes/shepherd/daemonControl.js
  23. 20
      routes/shepherd/dashboardUpdate.js
  24. 18
      routes/shepherd/debugLog.js
  25. 18
      routes/shepherd/downloadBins.js
  26. 29
      routes/shepherd/downloadPatch.js
  27. 9
      routes/shepherd/downloadZcparams.js
  28. 372
      routes/shepherd/elections.js
  29. 67
      routes/shepherd/electrum/auth.js
  30. 21
      routes/shepherd/electrum/balance.js
  31. 18
      routes/shepherd/electrum/block.js
  32. 47
      routes/shepherd/electrum/coins.js
  33. 500
      routes/shepherd/electrum/createtx-multi.js
  34. 86
      routes/shepherd/electrum/createtx-split.js
  35. 304
      routes/shepherd/electrum/createtx.js
  36. 9
      routes/shepherd/electrum/estimate.js
  37. 2
      routes/shepherd/electrum/interest.js
  38. 267
      routes/shepherd/electrum/keys.js
  39. 27
      routes/shepherd/electrum/listunspent.js
  40. 30
      routes/shepherd/electrum/merkle.js
  41. 81
      routes/shepherd/electrum/network.js
  42. 279
      routes/shepherd/electrum/transactions.js
  43. 9
      routes/shepherd/kickstart.js
  44. 27
      routes/shepherd/log.js
  45. 35
      routes/shepherd/pin.js
  46. 18
      routes/shepherd/quitDaemon.js
  47. 121
      routes/shepherd/rpc.js
  48. 6
      version
  49. 2
      version_build

2
LICENSE

@ -1,6 +1,6 @@
MIT License MIT License
Copyright (c) 2017 SuperNET Copyright (c) 2017 - 2018 SuperNET
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal of this software and associated documentation files (the "Software"), to deal

2
gui/EasyDEX-GUI

@ -1 +1 @@
Subproject commit 0092ac50871b6964e6462f3ba68ac7d1cd8c1421 Subproject commit 54f9dfc6e7b519a5a2295b49813cfd99be131cae

304
main.js

@ -17,7 +17,6 @@ const express = require('express');
const bodyParser = require('body-parser'); const bodyParser = require('body-parser');
const fsnode = require('fs'); const fsnode = require('fs');
const fs = require('fs-extra'); const fs = require('fs-extra');
const numCPUs = require('os').cpus().length;
const Promise = require('bluebird'); const Promise = require('bluebird');
const arch = require('arch'); const arch = require('arch');
@ -56,8 +55,8 @@ app.setVersion(appBasicInfo.version);
shepherd.createAgamaDirs(); shepherd.createAgamaDirs();
const appSessionHash = md5(Date.now().toString()); const appSessionHash = md5(Date.now().toString());
const _spvFees = shepherd.getSpvFees();
shepherd.writeLog(`app init ${appSessionHash}`);
shepherd.writeLog(`app info: ${appBasicInfo.name} ${appBasicInfo.version}`); shepherd.writeLog(`app info: ${appBasicInfo.name} ${appBasicInfo.version}`);
shepherd.writeLog('sys info:'); shepherd.writeLog('sys info:');
shepherd.writeLog(`totalmem_readable: ${formatBytes(os.totalmem())}`); shepherd.writeLog(`totalmem_readable: ${formatBytes(os.totalmem())}`);
@ -68,7 +67,9 @@ shepherd.writeLog(`platform: ${osPlatform}`);
shepherd.writeLog(`os_release: ${os.release()}`); shepherd.writeLog(`os_release: ${os.release()}`);
shepherd.writeLog(`os_type: ${os.type()}`); shepherd.writeLog(`os_type: ${os.type()}`);
if (process.argv.indexOf('devmode') > -1) {
shepherd.log(`app init ${appSessionHash}`); shepherd.log(`app init ${appSessionHash}`);
}
shepherd.log(`app info: ${appBasicInfo.name} ${appBasicInfo.version}`); shepherd.log(`app info: ${appBasicInfo.name} ${appBasicInfo.version}`);
shepherd.log('sys info:'); shepherd.log('sys info:');
shepherd.log(`totalmem_readable: ${formatBytes(os.totalmem())}`); shepherd.log(`totalmem_readable: ${formatBytes(os.totalmem())}`);
@ -89,7 +90,7 @@ shepherd.log(`app started in ${(appConfig.dev ? 'dev mode' : ' user mode')}`);
shepherd.writeLog(`app started in ${(appConfig.dev ? 'dev mode' : ' user mode')}`); shepherd.writeLog(`app started in ${(appConfig.dev ? 'dev mode' : ' user mode')}`);
shepherd.setConfKMD(); shepherd.setConfKMD();
shepherd.setConfKMD('CHIPS'); // shepherd.setConfKMD('CHIPS');
guiapp.use((req, res, next) => { guiapp.use((req, res, next) => {
res.header('Access-Control-Allow-Origin', appConfig.dev ? '*' : 'http://127.0.0.1:3000'); res.header('Access-Control-Allow-Origin', appConfig.dev ? '*' : 'http://127.0.0.1:3000');
@ -114,9 +115,8 @@ process.once('loaded', () => {
applicationVersion: `${app.getVersion().replace('version=', '')}-beta`, applicationVersion: `${app.getVersion().replace('version=', '')}-beta`,
copyright: 'Released under the MIT license', copyright: 'Released under the MIT license',
credits: 'SuperNET Team', credits: 'SuperNET Team',
}) });
} } else if (osPlatform === 'linux') {
if (osPlatform === 'linux') {
process.setFdLimit(appConfig.maxDescriptors.linux); process.setFdLimit(appConfig.maxDescriptors.linux);
} }
}); });
@ -149,9 +149,7 @@ const io = require('socket.io').listen(server);
const _zcashParamsExist = shepherd.zcashParamsExist(); const _zcashParamsExist = shepherd.zcashParamsExist();
let willQuitApp = false; let willQuitApp = false;
let mainWindow; let mainWindow;
let loadingWindow;
let appCloseWindow; let appCloseWindow;
let appSettingsWindow;
let closeAppAfterLoading = false; let closeAppAfterLoading = false;
let forceQuitApp = false; let forceQuitApp = false;
@ -165,123 +163,13 @@ if (os.platform() === 'win32') {
agamaIcon = path.join(__dirname, '/assets/icons/agama_app_icon.ico'); agamaIcon = path.join(__dirname, '/assets/icons/agama_app_icon.ico');
} }
function createLoadingWindow() {
mainWindow = null;
// initialise window
try {
loadingWindow = new BrowserWindow({
width: 500,
height: 355,
frame: false,
icon: agamaIcon,
show: false,
});
} catch(e) {}
loadingWindow.setResizable(false);
// check if agama is already running
portscanner.checkPortStatus(appConfig.agamaPort, '127.0.0.1', (error, status) => {
// Status is 'open' if currently in use or 'closed' if available
if (status === 'closed') {
server.listen(appConfig.agamaPort, () => {
shepherd.log(`guiapp and sockets.io are listening on port ${appConfig.agamaPort}`);
shepherd.writeLog(`guiapp and sockets.io are listening on port ${appConfig.agamaPort}`);
// start sockets.io
io.set('origins', appConfig.dev ? 'http://127.0.0.1:3000' : `http://127.0.0.1:${appConfig.agamaPort}`); // set origin
/*io.on('connection', function(client) {
shepherd.log('EDEX GUI is connected...');
shepherd.writeLog('EDEX GUI is connected...');
client.on('event', function(data) { // listen for client requests
shepherd.log(data);
});
client.on('disconnect', function(data) {
shepherd.log('EDEX GUI is disconnected');
});
client.on('join', function(data) {
shepherd.log(data);
client.emit('messages', 'Sockets server is listening');
});
});*/
});
} else {
willQuitApp = true;
server.listen(appConfig.agamaPort + 1, () => {
shepherd.log(`guiapp and sockets.io are listening on port ${appConfig.agamaPort + 1}`);
shepherd.writeLog(`guiapp and sockets.io are listening on port ${appConfig.agamaPort + 1}`);
});
loadingWindow.loadURL(`http://${appConfig.host}:${appConfig.agamaPort + 1}/gui/startup/agama-instance-error.html`);
shepherd.log('another agama app is already running');
}
});
shepherd.setIO(io); // pass sockets object to shepherd router
shepherd.setVar('appBasicInfo', appBasicInfo);
shepherd.setVar('appSessionHash', appSessionHash);
loadingWindow.createWindow = createWindow; // expose createWindow to front-end scripts
loadingWindow.appConfig = appConfig;
loadingWindow.forseCloseApp = forseCloseApp;
loadingWindow.createAppSettingsWindow = createAppSettingsWindow;
loadingWindow.startKMDNative = shepherd.startKMDNative;
loadingWindow.startSPV = shepherd.startSPV;
loadingWindow.arch = arch();
// load our index.html (i.e. easyDEX GUI)
loadingWindow.loadURL(`http://${appConfig.host}:${appConfig.agamaPort}/gui/startup`);
loadingWindow.webContents.on('did-finish-load', () => {
setTimeout(() => {
loadingWindow.show();
}, 40);
});
shepherd.writeLog('show loading window');
loadingWindow.on('hide', () => {
// our app does not have multiwindow - so we dereference the window object instead of
// putting them into an window_arr
loadingWindow = null;
});
loadingWindow.on('close', (e) => {
if (!forseCloseApp) {
if (willQuitApp) {
/* the user tried to quit the app */
loadingWindow = null;
} else {
/* the user only tried to close the window */
closeAppAfterLoading = true;
e.preventDefault();
}
}
});
}
// close app // close app
function forseCloseApp() { function forseCloseApp() {
forceQuitApp = true; forceQuitApp = true;
app.quit(); app.quit();
} }
function setDefaultAppSettings() { app.on('ready', () => createWindow('open', process.argv.indexOf('dexonly') > -1 ? true : null));
shepherd.saveLocalAppConf(_defaultAppSettings);
}
function updateAppSettings(_settings) {
shepherd.saveLocalAppConf(_settings);
appConfig = _settings;
}
if (process.argv.indexOf('dexonly') > -1) {
app.on('ready', createLoadingWindow);
setTimeout(() => {
createWindow('open', true);
}, 500);
} else {
app.on('ready', createLoadingWindow);
}
function createAppCloseWindow() { function createAppCloseWindow() {
// initialise window // initialise window
@ -304,66 +192,17 @@ function createAppCloseWindow() {
}); });
} }
function reloadSettingsWindow() {
appSettingsWindow.loadURL(`http://${appConfig.host}:${appConfig.agamaPort}/gui/startup/app-settings.html`);
}
function createAppSettingsWindow() {
// initialise window
appSettingsWindow = new BrowserWindow({ // dirty hack to prevent main window flash on quit
width: 750,
height: 610,
frame: false,
icon: agamaIcon,
show: false,
});
appSettingsWindow.setResizable(false);
appSettingsWindow.appConfig = appConfig;
appSettingsWindow.appConfigSchema = shepherd.appConfigSchema;
appSettingsWindow.defaultAppSettings = _defaultAppSettings;
appSettingsWindow.destroyAppSettingsWindow = destroyAppSettingsWindow;
appSettingsWindow.reloadSettingsWindow = reloadSettingsWindow;
appSettingsWindow.testLocation = shepherd.testLocation;
appSettingsWindow.setDefaultAppSettings = setDefaultAppSettings;
appSettingsWindow.updateAppSettings = updateAppSettings;
appSettingsWindow.testBins = shepherd.testBins;
appSettingsWindow.zcashParamsExist = _zcashParamsExist;
appSettingsWindow.loadURL(`http://${appConfig.host}:${appConfig.agamaPort}/gui/startup/app-settings.html`);
appSettingsWindow.webContents.on('did-finish-load', () => {
setTimeout(() => {
appSettingsWindow.show();
}, 40);
});
}
function destroyAppSettingsWindow() {
appSettingsWindow.hide();
appSettingsWindow = null;
}
function createWindow(status, hideLoadingWindow) { function createWindow(status, hideLoadingWindow) {
if (appSettingsWindow) { if (process.argv.indexOf('spvcoins=all/add-all') > -1) {
destroyAppSettingsWindow(); shepherd.startSPV('kmd');
} }
if (status === 'open') { if (status === 'open') {
require(path.join(__dirname, 'private/mainmenu')); require(path.join(__dirname, 'private/mainmenu'));
// initialise window
mainWindow = new BrowserWindow({ // dirty hack to prevent main window flash on quit
width: closeAppAfterLoading ? 1 : 1280,
height: closeAppAfterLoading ? 1 : 800,
icon: agamaIcon,
show: false,
});
if (closeAppAfterLoading) { if (closeAppAfterLoading) {
mainWindow = null; mainWindow = null;
loadingWindow = null; loadingWindow = null;
pm2Exit();
} }
const staticMenu = Menu.buildFromTemplate([ // if static const staticMenu = Menu.buildFromTemplate([ // if static
@ -383,6 +222,35 @@ function createWindow(status, hideLoadingWindow) {
{ role: 'selectall' }, { role: 'selectall' },
]); ]);
// check if agama is already running
portscanner.checkPortStatus(appConfig.agamaPort, '127.0.0.1', (error, status) => {
// Status is 'open' if currently in use or 'closed' if available
if (status === 'closed') {
server.listen(appConfig.agamaPort, () => {
shepherd.log(`guiapp and sockets.io are listening on port ${appConfig.agamaPort}`);
shepherd.writeLog(`guiapp and sockets.io are listening on port ${appConfig.agamaPort}`);
// start sockets.io
io.set('origins', appConfig.dev ? 'http://127.0.0.1:3000' : `http://127.0.0.1:${appConfig.agamaPort}`); // set origin
});
// initialise window
mainWindow = new BrowserWindow({ // dirty hack to prevent main window flash on quit
width: closeAppAfterLoading ? 1 : 1280,
height: closeAppAfterLoading ? 1 : 850,
icon: agamaIcon,
show: false,
});
if (appConfig.dev) {
mainWindow.loadURL('http://127.0.0.1:3000');
} else {
mainWindow.loadURL(`http://${appConfig.host}:${appConfig.agamaPort}/gui/EasyDEX-GUI/react/build`);
}
shepherd.setIO(io); // pass sockets object to shepherd router
shepherd.setVar('appBasicInfo', appBasicInfo);
shepherd.setVar('appSessionHash', appSessionHash);
// load our index.html (i.e. easyDEX GUI) // load our index.html (i.e. easyDEX GUI)
shepherd.writeLog('show edex gui'); shepherd.writeLog('show edex gui');
mainWindow.appConfig = appConfig; mainWindow.appConfig = appConfig;
@ -407,24 +275,62 @@ function createWindow(status, hideLoadingWindow) {
mainWindow.activeSection = 'wallets'; mainWindow.activeSection = 'wallets';
mainWindow.argv = process.argv; mainWindow.argv = process.argv;
mainWindow.getAssetChainPorts = shepherd.getAssetChainPorts; mainWindow.getAssetChainPorts = shepherd.getAssetChainPorts;
mainWindow.spvFees = _spvFees;
mainWindow.startSPV = shepherd.startSPV;
mainWindow.startKMDNative = shepherd.startKMDNative;
mainWindow.addressVersionCheck = shepherd.addressVersionCheck;
mainWindow.getCoinByPub = shepherd.getCoinByPub;
mainWindow.resetSettings = function() { shepherd.saveLocalAppConf(__defaultAppSettings) };
mainWindow.createSeed = {
triggered: false,
firstLoginPH: null,
secondaryLoginPH: null,
};
if (appConfig.dev) { for (let i = 0; i < process.argv.length; i++) {
mainWindow.loadURL('http://127.0.0.1:3000'); if (process.argv[i].indexOf('nvote') > -1) {
console.log(`notary node elections chain ${process.argv[i].replace('nvote=', '')}`);
mainWindow.nnVoteChain = process.argv[i].replace('nvote=', '');
}
}
} else { } else {
mainWindow.loadURL(`http://${appConfig.host}:${appConfig.agamaPort}/gui/EasyDEX-GUI/react/build`); mainWindow = new BrowserWindow({
width: 500,
height: 355,
frame: false,
icon: agamaIcon,
show: false,
});
mainWindow.setResizable(false);
mainWindow.forseCloseApp = forseCloseApp;
willQuitApp = true;
server.listen(appConfig.agamaPort + 1, () => {
shepherd.log(`guiapp and sockets.io are listening on port ${appConfig.agamaPort + 1}`);
shepherd.writeLog(`guiapp and sockets.io are listening on port ${appConfig.agamaPort + 1}`);
});
mainWindow.loadURL(`http://${appConfig.host}:${appConfig.agamaPort + 1}/gui/startup/agama-instance-error.html`);
shepherd.log('another agama app is already running');
} }
mainWindow.webContents.on('did-finish-load', () => { mainWindow.webContents.on('did-finish-load', () => {
setTimeout(() => { setTimeout(() => {
mainWindow.show(); mainWindow.show();
if (hideLoadingWindow &&
loadingWindow) {
loadingWindow.hide();
}
}, 40); }, 40);
}); });
/*loadingWindow.on('close', (e) => {
if (!forseCloseApp) {
if (willQuitApp) {
loadingWindow = null;
} else {
closeAppAfterLoading = true;
e.preventDefault();
}
}
});*/
mainWindow.webContents.on('context-menu', (e, params) => { // context-menu returns params mainWindow.webContents.on('context-menu', (e, params) => { // context-menu returns params
const { selectionText, isEditable } = params; // params obj const { selectionText, isEditable } = params; // params obj
@ -491,7 +397,9 @@ function createWindow(status, hideLoadingWindow) {
let _appClosingInterval; let _appClosingInterval;
// shepherd.killRogueProcess('marketmaker'); if (process.argv.indexOf('dexonly') > -1) {
shepherd.killRogueProcess('marketmaker');
}
if (!Object.keys(shepherd.coindInstanceRegistry).length || if (!Object.keys(shepherd.coindInstanceRegistry).length ||
!appConfig.stopNativeDaemonsOnQuit) { !appConfig.stopNativeDaemonsOnQuit) {
closeApp(); closeApp();
@ -506,10 +414,11 @@ function createWindow(status, hideLoadingWindow) {
} }
} }
// if window closed we kill iguana proc // close app
mainWindow.on('closed', () => { mainWindow.on('closed', () => {
appExit(); appExit();
}); });
});
} }
} }
@ -521,53 +430,34 @@ app.on('window-all-closed', () => {
/*if (process.platform !== 'darwin' || process.platform !== 'linux' || process.platform !== 'win32') { /*if (process.platform !== 'darwin' || process.platform !== 'linux' || process.platform !== 'win32') {
app.quit() app.quit()
}*/ }*/
}) });
// Emitted before the application starts closing its windows. // Emitted before the application starts closing its windows.
// Calling event.preventDefault() will prevent the default behaviour, which is terminating the application. // Calling event.preventDefault() will prevent the default behaviour, which is terminating the application.
app.on('before-quit', (event) => { app.on('before-quit', (event) => {
shepherd.log('before-quit'); shepherd.log('before-quit');
// shepherd.killRogueProcess('marketmaker'); if (process.argv.indexOf('dexonly') > -1) {
shepherd.killRogueProcess('marketmaker');
/*if (!forceQuitApp && }
mainWindow === null &&
loadingWindow != null) { // mainWindow not intitialised and loadingWindow not dereferenced
// loading window is still open
shepherd.log('before-quit prevented');
shepherd.writeLog('quit app after loading is done');
closeAppAfterLoading = true;
// obsolete(?)
let code = `$('#loading_status_text').html('Preparing to shutdown the wallet.<br/>Please wait while all daemons are closed...')`;
loadingWindow.webContents.executeJavaScript(code);
event.preventDefault();
}*/
}); });
// Emitted when all windows have been closed and the application will quit. // Emitted when all windows have been closed and the application will quit.
// Calling event.preventDefault() will prevent the default behaviour, which is terminating the application. // Calling event.preventDefault() will prevent the default behaviour, which is terminating the application.
app.on('will-quit', (event) => { app.on('will-quit', (event) => {
if (!forceQuitApp && if (!forceQuitApp) {
mainWindow === null &&
loadingWindow != null) {
// loading window is still open // loading window is still open
shepherd.log('will-quit while loading window active'); shepherd.log('will-quit while loading window active');
event.preventDefault(); // event.preventDefault();
} }
}); });
// Emitted when the application is quitting. // Emitted when the application is quitting.
// Calling event.preventDefault() will prevent the default behaviour, which is terminating the application. // Calling event.preventDefault() will prevent the default behaviour, which is terminating the application.
app.on('quit', (event) => { app.on('quit', (event) => {
if (!forceQuitApp && if (!forceQuitApp) {
mainWindow === null &&
loadingWindow != null) {
shepherd.log('quit while loading window active'); shepherd.log('quit while loading window active');
event.preventDefault(); // event.preventDefault();
} }
})
app.on('activate', () => {
if (mainWindow === null) {}
}); });
app.commandLine.appendSwitch('ignore-certificate-errors'); // dirty hack app.commandLine.appendSwitch('ignore-certificate-errors'); // dirty hack

22
package.json

@ -1,7 +1,7 @@
{ {
"name": "agama-app", "name": "agama-app",
"productName": "Agama", "productName": "Agama",
"version": "0.2.24", "version": "0.2.29",
"description": "Agama Wallet Desktop App", "description": "Agama Wallet Desktop App",
"main": "main.js", "main": "main.js",
"scripts": { "scripts": {
@ -10,32 +10,38 @@
"make-rpm": "node make-rpm.js", "make-rpm": "node make-rpm.js",
"make-deb": "node make-deb.js" "make-deb": "node make-deb.js"
}, },
"repository": "https://github.com/SuperNETorg/Agama/", "repository": "https://github.com/KomodoPlatform/Agama/",
"homepage": "http://supernet.org", "homepage": "http://supernet.org",
"keywords": [ "keywords": [
"agama", "agama",
"SuperNET", "SuperNET",
"komodo", "komodo",
"pax" "multicoin",
"wallet",
"spv"
], ],
"author": "SuperNET", "author": "SuperNET Team",
"license": "MIT", "license": "MIT",
"devDependencies": { "devDependencies": {
"electron": "1.6.5", "electron": "1.7.10",
"electron-installer-debian": "^0.6.0", "electron-installer-debian": "^0.6.0",
"electron-installer-redhat": "^0.5.0" "electron-installer-redhat": "^0.5.0"
}, },
"dependencies": { "dependencies": {
"adm-zip": "^0.4.7", "adm-zip": "^0.4.7",
"arch": "^2.1.0", "arch": "^2.1.0",
"async": "^2.6.0",
"bigi": "^1.4.2",
"bip39": "^2.4.0", "bip39": "^2.4.0",
"bitcoinjs-lib": "^3.2.0", "bitcoinforksjs-lib": "git://github.com/bitcoinjs/bitcoinjs-lib#opt-in-bitcoincash-sighash",
"bitcoinjs-lib": "git://github.com/SuperNETorg/bitcoinjs-lib",
"bitcoinjs-lib-zcash": "git://github.com/pbca26/bitcoinjs-lib#zcash",
"bitcoinjs-lib-pos": "git://github.com/KomodoPlatform/bitcoinjs-lib-pos",
"bluebird": "^3.4.7", "bluebird": "^3.4.7",
"body-parser": "^1.15.2", "body-parser": "^1.15.2",
"buffer-reverse": "^1.0.1", "buffer-reverse": "^1.0.1",
"coinkey": "^2.0.0",
"coinselect": "github:bitcoinjs/coinselect", "coinselect": "github:bitcoinjs/coinselect",
"electron": "1.6.5", "electron": "1.8.2",
"express": "^4.14.0", "express": "^4.14.0",
"fix-path": "^2.1.0", "fix-path": "^2.1.0",
"fs-extra": "^4.0.2", "fs-extra": "^4.0.2",

126
private/kmdcli.js

@ -1,126 +0,0 @@
/*
* Copyright (c) 2015 Satinderjit Singh
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
*/
/*
* Agama komodo-cli paths
*
*/
var child_process = require('child_process'),
path = require('path'),
os = require('os');
if (os.platform() === 'darwin') {
var komodocliBin = path.join(__dirname, '../assets/bin/osx/komodo-cli'),
zcashcliBin = '/Applications/ZCashSwingWalletUI.app/Contents/MacOS/zcash-cli';
}
if (os.platform() === 'linux') {
var komodocliBin = path.join(__dirname, '../assets/bin/linux64/komodo-cli');
}
if (os.platform() === 'win32') {
var komodocliBin = path.join(__dirname, '../assets/bin/win64/komodo-cli.exe'),
komodocliBin = path.normalize(komodocliBin);
}
console.log(komodocliBin)
/**
* The **komodo-cli** command is used to get komodo api calls answer.
*
* @private
* @category kmdcli
*
*/
var kmdcli = module.exports = {
exec: child_process.exec,
command: command
};
/**
* Parses komodo-cli commands.
*
* @private
* @static
* @category kmdcli
* @param {function} callback The callback function.
*
*/
function parse_kmdcli_commands(callback) {
return function(error, stdout, stderr) {
if (error) callback(error, stderr);
else callback(error, stdout);
//console.log(stdout)
};
}
/**
* Parses komodo-cli commands.
*
* @private
* @static
* @category kmdcli
* @param {function} callback The callback function.
* @example
*
* var kmdcli = require('./kmdcli');
*
* kmdcli.command('getinfo', function(err, command) {
* console.log(command);
* });
*
* // =>
* {
* "version" : 1000550,
* "protocolversion" : 170002,
* "notarized" : 254740,
* "notarizedhash" : "01f4f1c46662ccca2e7fa9e7e38d4d2e4ced4402fa0f4fc116b8f004bb8cf272",
* "notarizedtxid" : "2b16e47a176f8c1886ca0268243f9b96f8b2db466ea26ae99873d5224bbf80b6",
* "walletversion" : 60000,
* "balance" : 32632.46167742,
* "interest" : 0.00478671,
* "blocks" : 254791,
* "longestchain" : 254791,
* "timeoffset" : 0,
* "tiptime" : 1490815616,
* "connections" : 8,
* "proxy" : "",
* "difficulty" : 707836.56791394,
* "testnet" : false,
* "keypoololdest" : 1482746526,
* "keypoolsize" : 101,
* "paytxfee" : 0.00000000,
* "relayfee" : 0.00001000,
* "errors" : "WARNING: check your network connection, 157 blocks received in the last 4 hours (240 expected)",
* "notaryid" : -1,
* "pubkey" : "000000000000000000000000000000000000000000000000000000000000000000"
* }
*
*/
function command(kmd_command, callback) {
if (callback) {
return this.exec(komodocliBin + " " + kmd_command,
parse_kmdcli_commands(callback));
}
}

36
private/mainmenu.js

@ -86,42 +86,18 @@ const template = [
} }
] ]
}, },
/*{ {
role: 'help', role: 'help',
label: 'Support', label: 'Debug',
submenu: [ submenu: [
{ {
label: 'Supernet.org', label: 'Reset settings',
click () { click (item, focusedWindow) {
if (process.platform === 'linux') { focusedWindow.resetSettings();
require('child_process').exec('xdg-open http://support.supernet.org');
} else {
shell.openExternal('http://support.supernet.org');
}
}
},
{
label: 'Slack',
click () {
if (process.platform === 'linux') {
require('child_process').exec('xdg-open https://sprnt.slack.com/messages/support');
} else {
shell.openExternal('https://sprnt.slack.com/messages/support');
}
} }
}, },
{
label: 'Github',
click () {
if (process.platform === 'linux') {
require('child_process').exec('xdg-open https://github.com/SuperNETorg/iguana/issues');
} else {
shell.openExternal('https://github.com/SuperNETorg/iguana/issues');
}
}
}
] ]
}*/ }
] ]
if (process.platform === 'darwin') { if (process.platform === 'darwin') {

24
routes/appConfig.js

@ -17,6 +17,9 @@ const appConfig = {
cliStopTimeout: 1000, cliStopTimeout: 1000,
failedRPCAttemptsThreshold: 10, failedRPCAttemptsThreshold: 10,
stopNativeDaemonsOnQuit: true, stopNativeDaemonsOnQuit: true,
lang: 'EN',
rpc2cli: false,
fiatRates: false,
}, },
schema: { schema: {
host: { host: {
@ -105,6 +108,27 @@ const appConfig = {
info: 'Number of allowed consequent RPC connect failures before the app marks native coin daemon as not running properly', info: 'Number of allowed consequent RPC connect failures before the app marks native coin daemon as not running properly',
type: 'number', type: 'number',
}, },
lang: {
display: true,
displayName: 'Language',
type: 'select',
data: [
{ name: 'EN', label: 'English' },
{ name: 'DE', label: 'German' }
],
},
rpc2cli: {
display: true,
displayName: 'Disable RPC',
info: 'Use CLI instead of RPC JSON server in native mode',
type: 'boolean',
},
fiatRates: {
display: true,
displayName: 'Fetch fiat rates',
info: 'Get coin fiat rates from atomicexplorer.com',
type: 'boolean',
},
}, },
}; };

289
routes/electrumjs/electrumServers.js

@ -1,9 +1,4 @@
let electrumServers = { let electrumServers = {
/*zcash: {
address: '173.212.225.176',
port: 50032,
proto: 'tcp',
},*/
coqui: { // !estimatefee coqui: { // !estimatefee
address: 'electrum1.cipig.net', address: 'electrum1.cipig.net',
port: 10011, port: 10011,
@ -141,6 +136,61 @@ let electrumServers = {
'electrum2.cipig.net:10014' 'electrum2.cipig.net:10014'
], ],
}, },
mgw: { // !estimatefee
address: 'electrum1.cipig.net',
port: 10015,
proto: 'tcp',
txfee: 10000,
abbr: 'MGW',
serverList: [
'electrum1.cipig.net:10015',
'electrum2.cipig.net:10015'
],
},
btch: { // !estimatefee
address: 'electrum1.cipig.net',
port: 10020,
proto: 'tcp',
txfee: 10000,
abbr: 'BTCH',
serverList: [
'electrum1.cipig.net:10020',
'electrum2.cipig.net:10020'
],
},
beer: { // !estimatefee
address: 'electrum1.cipig.net',
port: 10022,
proto: 'tcp',
txfee: 10000,
abbr: 'BEER',
serverList: [
'electrum1.cipig.net:10022',
'electrum2.cipig.net:10022'
],
},
pizza: { // !estimatefee
address: 'electrum1.cipig.net',
port: 10024,
proto: 'tcp',
txfee: 10000,
abbr: 'PIZZA',
serverList: [
'electrum1.cipig.net:10024',
'electrum2.cipig.net:10024'
],
},
vote: { // !estimatefee
address: 'electrum1.cipig.net',
port: 10021,
proto: 'tcp',
txfee: 10000,
abbr: 'VOTE',
serverList: [
'electrum1.cipig.net:10021',
'electrum2.cipig.net:10021'
],
},
jumblr: { // !estimatefee jumblr: { // !estimatefee
address: 'electrum1.cipig.net', address: 'electrum1.cipig.net',
port: 10004, port: 10004,
@ -169,13 +219,21 @@ let electrumServers = {
proto: 'tcp', proto: 'tcp',
txfee: 100000000, txfee: 100000000,
abbr: 'DOGE', abbr: 'DOGE',
serverList: [
'173.212.225.176:50015',
'136.243.45.140:50015'
],
}, },
viacoin: { // !estimatefee viacoin: { // !estimatefee
address: 'vialectrum.bitops.me', address: '173.212.225.176',
port: 50002, port: 50033,
proto: 'ssl', proto: 'tcp',
txfee: 100000, txfee: 100000,
abbr: 'VIA', abbr: 'VIA',
serverList: [
'173.212.225.176:50033',
'136.243.45.140:50033'
],
}, },
vertcoin: { vertcoin: {
address: '173.212.225.176', address: '173.212.225.176',
@ -183,6 +241,10 @@ let electrumServers = {
proto: 'tcp', proto: 'tcp',
txfee: 100000, txfee: 100000,
abbr: 'VTC', abbr: 'VTC',
serverList: [
'173.212.225.176:50088',
'136.243.45.140:50088'
],
}, },
namecoin: { namecoin: {
address: '173.212.225.176', address: '173.212.225.176',
@ -190,6 +252,10 @@ let electrumServers = {
proto: 'tcp', proto: 'tcp',
txfee: 100000, txfee: 100000,
abbr: 'NMC', abbr: 'NMC',
serverList: [
'173.212.225.176:50036',
'136.243.45.140:50036'
],
}, },
monacoin: { // !estimatefee monacoin: { // !estimatefee
address: '173.212.225.176', address: '173.212.225.176',
@ -197,13 +263,21 @@ let electrumServers = {
proto: 'tcp', proto: 'tcp',
txfee: 100000, txfee: 100000,
abbr: 'MONA', abbr: 'MONA',
serverList: [
'173.212.225.176:50002',
'136.243.45.140:50002'
],
}, },
litecoin: { litecoin: {
address: '173.212.225.176', address: '173.212.225.176',
port: 50012, port: 50012,
proto: 'tcp', proto: 'tcp',
txfee: 10000, txfee: 100000,
abbr: 'LTC', abbr: 'LTC',
serverList: [
'173.212.225.176:50012',
'136.243.45.140:50012'
],
}, },
faircoin: { faircoin: {
address: '173.212.225.176', address: '173.212.225.176',
@ -211,13 +285,21 @@ let electrumServers = {
proto: 'tcp', proto: 'tcp',
txfee: 1000000, txfee: 1000000,
abbr: 'FAIR', abbr: 'FAIR',
serverList: [
'173.212.225.176:50005',
'136.243.45.140:50005'
],
}, },
digibyte: { dgb: {
address: '173.212.225.176', address: '173.212.225.176',
port: 50022, port: 50022,
proto: 'tcp', proto: 'tcp',
txfee: 100000, txfee: 100000,
abbr: 'DGB', abbr: 'DGB',
serverList: [
'173.212.225.176:50022',
'136.243.45.140:50022'
],
}, },
dash: { dash: {
address: '173.212.225.176', address: '173.212.225.176',
@ -225,6 +307,10 @@ let electrumServers = {
proto: 'tcp', proto: 'tcp',
txfee: 10000, txfee: 10000,
abbr: 'DASH', abbr: 'DASH',
serverList: [
'173.212.225.176:50098',
'136.243.45.140:50098'
],
}, },
crown: { crown: {
address: '173.212.225.176', address: '173.212.225.176',
@ -232,12 +318,70 @@ let electrumServers = {
proto: 'tcp', proto: 'tcp',
txfee: 10000, txfee: 10000,
abbr: 'CRW', abbr: 'CRW',
serverList: [
'173.212.225.176:50041',
'136.243.45.140:50041'
],
}, },
bitcoin: { btc: {
address: '173.212.225.176', address: 'e-x.not.fyi',
port: 50001, port: 50001,
proto: 'tcp', proto: 'tcp',
abbr: 'BTC', abbr: 'BTC',
serverList: [
'mooo.not.fyi:50011',
'e-x.not.fyi:50001',
'vps.hsmiths.com:50001',
'us.electrum.be:50001',
'electrumx.bot.nu:50001',
'btc.asis.io:50001',
'electrum.backplanedns.org:50001',
'electrum.festivaldelhumor.org:50001'
],
},
btg: {
address: '173.212.225.176',
port: 10052,
proto: 'tcp',
abbr: 'BTG',
txfee: 10000,
serverList: [
'173.212.225.176:10052',
'94.130.224.11:10052'
],
},
blk: { // pos
address: 'electrum1.cipig.net',
port: 10054,
proto: 'tcp',
abbr: 'BLK',
txfee: 10000,
serverList: [
'electrum1.cipig.net:10054',
'electrum2.cipig.net:10054'
],
},
sib: {
address: 'electrum1.cipig.net',
port: 10050,
proto: 'tcp',
abbr: 'SIB',
txfee: 10000,
serverList: [
'electrum1.cipig.net:10050',
'electrum2.cipig.net:10050'
],
},
bch: {
address: 'electrum1.cipig.net',
port: 10051,
proto: 'tcp',
abbr: 'BCH',
txfee: 10000,
serverList: [
'electrum1.cipig.net:10051',
'electrum2.cipig.net:10051'
],
}, },
argentum: { // !estimatefee argentum: { // !estimatefee
address: '173.212.225.176', address: '173.212.225.176',
@ -245,18 +389,131 @@ let electrumServers = {
proto: 'tcp', proto: 'tcp',
txfee: 50000, txfee: 50000,
abbr: 'ARG', abbr: 'ARG',
serverList: [
'173.212.225.176:50081',
'136.243.45.140:50081'
],
}, },
chips: { // !estimatefee chips: { // !estimatefee
address: '173.212.225.176', address: 'electrum1.cipig.net',
port: 50076, port: 10053,
proto: 'tcp', proto: 'tcp',
txfee: 10000, txfee: 10000,
abbr: 'CHIPS', abbr: 'CHIPS',
serverList: [ serverList: [
'173.212.225.176:50076', 'electrum1.cipig.net:10053',
'136.243.45.140:50076' 'electrum2.cipig.net:10053'
],
},
zec: {
address: '173.212.225.176',
port: 50032,
proto: 'tcp',
txfee: 10000,
abbr: 'ZEC',
serverList: [
'173.212.225.176:50032',
'136.243.45.140:50032'
],
},
hush: {
address: '173.212.225.176',
port: 50013,
proto: 'tcp',
txfee: 10000,
abbr: 'HUSH',
serverList: [
'173.212.225.176:50013',
'136.243.45.140:50013'
],
},
xmy: {
address: 'cetus.cryptap.us',
port: 50004,
proto: 'ssl',
txfee: 5000,
abbr: 'XMY',
serverList: [
'cetus.cryptap.us:50004',
'kraken.cryptap.us:50004'
],
},
zcl: {
address: 'electrum1.cipig.net',
port: 50055,
proto: 'tcp',
txfee: 1000,
abbr: 'ZCL',
serverList: [
'electrum1.cipig.net:10055',
'electrum2.cipig.net:10055'
],
},
hodlc: {
address: 'hodl.amit177.cf',
port: 17989,
proto: 'tcp',
txfee: 5000,
abbr: 'HODLC',
serverList: [
'hodl.amit177.cf:17989',
'hodl2.amit177.cf:17898'
],
},
btx: {
address: 'electrum1.cipig.net',
port: 10057,
proto: 'tcp',
txfee: 50000,
abbr: 'BTX',
serverList: [
'electrum1.cipig.net:10057',
'electrum2.cipig.net:10057'
],
},
btcz: {
address: 'electrum1.cipig.net',
port: 10056,
proto: 'tcp',
txfee: 10000,
abbr: 'BTCZ',
serverList: [
'electrum1.cipig.net:10056',
'electrum2.cipig.net:10056'
],
},
grs: {
address: 'electrum10.groestlcoin.org',
port: 50001,
proto: 'tcp',
txfee: 50000,
abbr: 'GRS',
serverList: [
'electrum10.groestlcoin.org:50001',
'electrum11.groestlcoin.org:50001'
],
},
qtum: {
address: 's1.qtum.info',
port: 50001,
proto: 'tcp',
txfee: 400000,
abbr: 'QTUM',
serverList: [
's1.qtum.info:50001',
's2.qtum.info:50001'
], ],
}, },
}; };
electrumServers.crw = electrumServers.crown;
electrumServers.fair = electrumServers.faircoin;
electrumServers.arg = electrumServers.argentum;
electrumServers.ltc = electrumServers.litecoin;
electrumServers.mona = electrumServers.litecoin;
electrumServers.nmc = electrumServers.namecoin;
electrumServers.vtc = electrumServers.vertcoin;
electrumServers.via = electrumServers.viacoin;
electrumServers.doge = electrumServers.dogecoin;
module.exports = electrumServers; module.exports = electrumServers;

173
routes/electrumjs/electrumjs.networks.js

@ -2,8 +2,8 @@
var bitcoin = require('bitcoinjs-lib'); var bitcoin = require('bitcoinjs-lib');
var networks = exports; var networks = exports;
Object.keys(bitcoin.networks).forEach(function(key){ Object.keys(bitcoin.networks).forEach((key) => {
networks[key] = bitcoin.networks[key] networks[key] = bitcoin.networks[key];
}); });
networks.litecoin = { networks.litecoin = {
@ -15,8 +15,8 @@ networks.litecoin = {
pubKeyHash: 0x30, pubKeyHash: 0x30,
scriptHash: 0x32, scriptHash: 0x32,
wif: 0xb0, wif: 0xb0,
dustThreshold: 0, // https://github.com/litecoin-project/litecoin/blob/v0.8.7.2/src/main.cpp#L360-L365 dustThreshold: 0 // https://github.com/litecoin-project/litecoin/blob/v0.8.7.2/src/main.cpp#L360-L365
} };
networks.dogecoin = { networks.dogecoin = {
messagePrefix: '\x19Dogecoin Signed Message:\n', messagePrefix: '\x19Dogecoin Signed Message:\n',
@ -27,7 +27,7 @@ networks.dogecoin = {
pubKeyHash: 0x1e, pubKeyHash: 0x1e,
scriptHash: 0x16, scriptHash: 0x16,
wif: 0x9e, wif: 0x9e,
dustThreshold: 0 // https://github.com/dogecoin/dogecoin/blob/v1.7.1/src/core.h#L155-L160 dustThreshold: 0, // https://github.com/dogecoin/dogecoin/blob/v1.7.1/src/core.h#L155-L160
}; };
// https://github.com/monacoinproject/monacoin/blob/master-0.10/src/chainparams.cpp#L161 // https://github.com/monacoinproject/monacoin/blob/master-0.10/src/chainparams.cpp#L161
@ -192,8 +192,81 @@ networks.chips = {
dustThreshold: 1000, dustThreshold: 1000,
}; };
/*networks.zcash = { networks.btg = {
messagePrefix: '\x19BitcoinGold Signed Message:\n',
bip32: {
public: 0x0488b21e,
private: 0x0488ade4,
},
pubKeyHash: 0x26,
scriptHash: 0x17,
wif: 0x80,
dustThreshold: 1000,
};
networks.bch = {
messagePrefix: '\x19BitcoinCash Signed Message:\n',
bip32: {
public: 0x0488b21e,
private: 0x0488ade4,
},
pubKeyHash: 0x0,
scriptHash: 0x5,
wif: 0x80,
dustThreshold: 1000,
};
networks.blk = {
messagePrefix: '\x19BlackCoin Signed Message:\n',
bip32: {
public: 0x0488b21e,
private: 0x0488ade4,
},
pubKeyHash: 0x19,
scriptHash: 0x55,
wif: 0x99,
dustThreshold: 1000,
isPoS: true,
};
networks.sib = {
messagePrefix: '\x19SibCoin Signed Message:\n',
bip32: {
public: 0x0488b21e,
private: 0x0488ade4,
},
pubKeyHash: 0x3f,
scriptHash: 0x28,
wif: 0x80,
dustThreshold: 1000,
};
networks.zcash = {
messagePrefix: '\x19Zcash Signed Message:\n', messagePrefix: '\x19Zcash Signed Message:\n',
bip32: {
public: 0x0488b21e,
private: 0x05358394,
},
pubKeyHash: 0x1cb8,
scriptHash: 0x1cbd,
wif: 0x80,
dustThreshold: 1000,
};
networks.hush = {
messagePrefix: '\x19Hush Signed Message:\n',
bip32: {
public: 0x0488b21e,
private: 0x0488ade4,
},
pubKeyHash: 0x1cb8,
scriptHash: 0x1cbd,
wif: 0x80,
dustThreshold: 1000,
};
networks.zcl = {
messagePrefix: '\x19Zclassic Signed Message:\n',
bip32: { bip32: {
public: 0x0488b21e, public: 0x0488b21e,
private: 0x0488ade4, private: 0x0488ade4,
@ -202,4 +275,90 @@ networks.chips = {
scriptHash: 0x1cbd, scriptHash: 0x1cbd,
wif: 0x80, wif: 0x80,
dustThreshold: 1000, dustThreshold: 1000,
};*/ };
networks.xmy = {
messagePrefix: '\x19Myriad Signed Message:\n',
bip32: {
public: 0x0488b21e,
private: 0x0488ade4,
},
pubKeyHash: 0x32,
scriptHash: 0x9,
wif: 0xB2,
dustThreshold: 1000,
};
networks.hodlc = {
messagePrefix: '\x19Hodlc Signed Message:\n',
bip32: {
public: 0x0488b21e,
private: 0x0488ade4,
},
pubKeyHash: 0x28,
scriptHash: 0x5,
wif: 0x28 + 128,
dustThreshold: 1000,
};
networks.qtum = {
messagePrefix: '\x19Qtum Signed Message:\n',
bip32: {
public: 0x0488b21e,
private: 0x0488ade4,
},
pubKeyHash: 0x3A,
scriptHash: 0x32,
wif: 0x80,
dustThreshold: 1000,
};
networks.btx = {
messagePrefix: '\x19Bitcore Signed Message:\n',
bip32: {
public: 0x0488b21e,
private: 0x0488ade4,
},
pubKeyHash: 0x0,
scriptHash: 0x5,
wif: 0x80,
dustThreshold: 1000,
};
networks.btcz = {
messagePrefix: '\x19BitcoinZ Signed Message:\n',
bip32: {
public: 0x0488b21e,
private: 0x0488ade4,
},
pubKeyHash: 0x1cb8,
scriptHash: 0x1cbd,
wif: 0x80,
dustThreshold: 1000,
};
networks.grs = { // fails to gen a proper addr
messagePrefix: '\x19Groestlcoin Signed Message:\n',
bip32: {
public: 0x0488b21e,
private: 0x0488ade4,
},
pubKeyHash: 0x24,
scriptHash: 0x5,
wif: 0x80,
dustThreshold: 1000,
};
networks.btc = networks.bitcoin;
networks.crw = networks.crown;
networks.dgb = networks.digibyte;
networks.arg = networks.argentum;
networks.zec = networks.zcash;
networks.nmc = networks.namecoin;
networks.ltc = networks.litecoin;
networks.vtc = networks.vertcoin;
networks.via = networks.viacoin;
networks.fair = networks.faircoin;
networks.doge = networks.dogecoin;
networks.kmd = networks.komodo;
networks.mona = networks.monacoin;

120
routes/electrumjs/electrumjs.txdecoder-2bytes.js

@ -0,0 +1,120 @@
/*
MIT License
Copyright (c) 2017 Yuki Akiyama, SuperNET
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
var bitcoin = require('bitcoinjs-lib-zcash');
var decodeFormat = function(tx) {
var result = {
txid: tx.getId(),
version: tx.version,
locktime: tx.locktime,
};
return result;
}
var decodeInput = function(tx) {
var result = [];
tx.ins.forEach(function(input, n) {
var vin = {
txid: input.hash.reverse().toString('hex'),
n: input.index,
script: bitcoin.script.toASM(input.script),
sequence: input.sequence,
};
result.push(vin);
});
return result;
}
var decodeOutput = function(tx, network) {
var format = function(out, n, network) {
var vout = {
satoshi: out.value,
value: (1e-8 * out.value).toFixed(8),
n: n,
scriptPubKey: {
asm: bitcoin.script.toASM(out.script),
hex: out.script.toString('hex'),
type: bitcoin.script.classifyOutput(out.script),
addresses: [],
},
};
switch(vout.scriptPubKey.type) {
case 'pubkeyhash':
vout.scriptPubKey.addresses.push(bitcoin.address.fromOutputScript(out.script, network));
break;
case 'pubkey':
const pubKeyBuffer = new Buffer(vout.scriptPubKey.asm.split(' ')[0], 'hex');
vout.scriptPubKey.addresses.push(bitcoin.ECPair.fromPublicKeyBuffer(pubKeyBuffer, network).getAddress());
break;
case 'scripthash':
vout.scriptPubKey.addresses.push(bitcoin.address.fromOutputScript(out.script, network));
break;
}
return vout;
}
var result = [];
tx.outs.forEach(function(out, n) {
result.push(format(out, n, network));
});
return result;
}
var TxDecoder = module.exports = function(rawtx, network) {
try {
const _tx = bitcoin.Transaction.fromHex(rawtx);
return {
tx: _tx,
network: network,
format: decodeFormat(_tx),
inputs: decodeInput(_tx),
outputs: decodeOutput(_tx, network),
};
} catch (e) {
return false;
}
}
TxDecoder.prototype.decode = function() {
var result = {};
var self = this;
Object.keys(self.format).forEach(function(key) {
result[key] = self.format[key];
});
result.outputs = self.outputs;
return result;
}

123
routes/electrumjs/electrumjs.txdecoder-pos.js

@ -0,0 +1,123 @@
/*
MIT License
Copyright (c) 2017 Yuki Akiyama, SuperNET
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
var bitcoin = require('bitcoinjs-lib-pos');
var script = require('bitcoinjs-lib-pos/src/script');
var address = require('bitcoinjs-lib-pos/src/address');
var bitcoinJS = require('bitcoinjs-lib');
var decodeFormat = function(tx) {
var result = {
txid: tx.getId(),
version: tx.version,
locktime: tx.locktime,
};
return result;
}
var decodeInput = function(tx) {
var result = [];
tx.ins.forEach(function(input, n) {
var vin = {
txid: input.hash.reverse().toString('hex'),
n: input.index,
script: script.fromHex(input.hash),
sequence: input.sequence,
};
result.push(vin);
});
return result;
}
var decodeOutput = function(tx, network) {
var format = function(out, n, network) {
var vout = {
satoshi: out.value,
value: (1e-8 * out.value).toFixed(8),
n: n,
scriptPubKey: {
asm: bitcoinJS.script.toASM(out.script.chunks),
hex: out.script.toHex(),
type: bitcoin.scripts.classifyOutput(out.script),
addresses: [],
},
};
switch(vout.scriptPubKey.type) {
case 'pubkeyhash':
vout.scriptPubKey.addresses.push(address.fromOutputScript(out.script, network));
break;
case 'pubkey':
const pubKeyBuffer = new Buffer(vout.scriptPubKey.asm.split(' ')[0], 'hex');
vout.scriptPubKey.addresses.push(bitcoin.ECPair.fromPublicKeyBuffer(pubKeyBuffer, network).getAddress());
break;
case 'scripthash':
vout.scriptPubKey.addresses.push(address.fromOutputScript(out.script, network));
break;
}
return vout;
}
var result = [];
tx.outs.forEach(function(out, n) {
result.push(format(out, n, network));
});
return result;
}
var TxDecoder = module.exports = function(rawtx, network) {
try {
const _tx = bitcoin.Transaction.fromHex(rawtx, network);
return {
tx: _tx,
network: network,
format: decodeFormat(_tx),
inputs: decodeInput(_tx),
outputs: decodeOutput(_tx, network),
};
} catch (e) {
return false;
}
}
TxDecoder.prototype.decode = function() {
var result = {};
var self = this;
Object.keys(self.format).forEach(function(key) {
result[key] = self.format[key];
});
result.outputs = self.outputs;
return result;
}

40
routes/ports.js

@ -1,6 +1,10 @@
// default daemon ports
const assetChainPorts = { const assetChainPorts = {
'komodod': '7771', 'komodod': '7771',
'markermaker': '7783', 'markermaker': '7783',
'PIZZA': '11608',
'BEER': '8923',
'CHIPS': '57776', 'CHIPS': '57776',
'SUPERNET': '11341', 'SUPERNET': '11341',
'REVS': '10196', 'REVS': '10196',
@ -20,38 +24,10 @@ const assetChainPorts = {
'KV': '8299', 'KV': '8299',
'CEAL': '11116', 'CEAL': '11116',
'MESH': '9455', 'MESH': '9455',
'USD': '13967', 'AXO': '12927',
'CHF': '15312', 'ETOMIC': '10271',
'CAD': '8720', 'VOTE': '8012',
'BRL': '9914', 'BTCH': '8800',
'BGN': '9110',
'AUD': '8045',
'PLN': '13493',
'PHP': '11181',
'NZD': '10915',
'NOK': '11588',
'MYR': '10688',
'MXN': '13970',
'KRW': '14020',
'JPY': '13145',
'INR': '10536',
'ILS': '14638',
'IDR': '14459',
'HKD': '15409',
'HUF': '13699',
'HRK': '12617',
'GBP': '11505',
'EUR': '8065',
'DKK': '13830',
'CNY': '10384',
'ZAR': '15160',
'TRY': '13924',
'THB': '11847',
'SGD': '14475',
'SEK': '11447',
'RON': '8675',
'RUB': '8199',
'CZK': '9482'
}; };
module.exports = assetChainPorts; module.exports = assetChainPorts;

12
routes/shepherd.js

@ -18,7 +18,6 @@ shepherd.Promise = require('bluebird');
shepherd.exec = require('child_process').exec; shepherd.exec = require('child_process').exec;
shepherd.execFile = require('child_process').execFile; shepherd.execFile = require('child_process').execFile;
shepherd.sha256 = require('sha256'); shepherd.sha256 = require('sha256');
shepherd.CoinKey = require('coinkey');
shepherd.bitcoinJS = require('bitcoinjs-lib'); shepherd.bitcoinJS = require('bitcoinjs-lib');
shepherd.coinSelect = require('coinselect'); shepherd.coinSelect = require('coinselect');
shepherd.fixPath = require('fix-path'); shepherd.fixPath = require('fix-path');
@ -38,6 +37,8 @@ shepherd.rpcConf = {};
shepherd.appRuntimeLog = []; shepherd.appRuntimeLog = [];
shepherd.appRuntimeSPVLog = []; shepherd.appRuntimeSPVLog = [];
shepherd.lockDownAddCoin = false; shepherd.lockDownAddCoin = false;
// dex cache
shepherd.mmupass = null; shepherd.mmupass = null;
shepherd.mmRatesInterval = null; shepherd.mmRatesInterval = null;
shepherd.mmPublic = { shepherd.mmPublic = {
@ -60,9 +61,10 @@ shepherd.electrumCoins = {
}; };
shepherd.electrumKeys = {}; shepherd.electrumKeys = {};
shepherd.electrumCache = {};
shepherd.electrumJSCore = require('./electrumjs/electrumjs.core.js'); shepherd.electrumJSCore = require('./electrumjs/electrumjs.core.js');
shepherd.electrumJSNetworks = require('./electrumjs/electrumjs.networks.js'); shepherd.electrumJSNetworks = require('./electrumjs/electrumjs.networks.js');
shepherd.electrumJSTxDecoder = require('./electrumjs/electrumjs.txdecoder.js');
shepherd.electrumServers = require('./electrumjs/electrumServers.js'); shepherd.electrumServers = require('./electrumjs/electrumServers.js');
shepherd.CONNECTION_ERROR_OR_INCOMPLETE_DATA = 'connection error or incomplete data'; shepherd.CONNECTION_ERROR_OR_INCOMPLETE_DATA = 'connection error or incomplete data';
@ -96,6 +98,8 @@ shepherd = require('./shepherd/electrum/balance.js')(shepherd);
shepherd = require('./shepherd/electrum/transactions.js')(shepherd); shepherd = require('./shepherd/electrum/transactions.js')(shepherd);
shepherd = require('./shepherd/electrum/block.js')(shepherd); shepherd = require('./shepherd/electrum/block.js')(shepherd);
shepherd = require('./shepherd/electrum/createtx.js')(shepherd); shepherd = require('./shepherd/electrum/createtx.js')(shepherd);
shepherd = require('./shepherd/electrum/createtx-split.js')(shepherd);
shepherd = require('./shepherd/electrum/createtx-multi.js')(shepherd);
shepherd = require('./shepherd/electrum/interest.js')(shepherd); shepherd = require('./shepherd/electrum/interest.js')(shepherd);
shepherd = require('./shepherd/electrum/listunspent.js')(shepherd); shepherd = require('./shepherd/electrum/listunspent.js')(shepherd);
shepherd = require('./shepherd/electrum/estimate.js')(shepherd); shepherd = require('./shepherd/electrum/estimate.js')(shepherd);
@ -109,7 +113,6 @@ shepherd = require('./shepherd/dex/electrumServersList.js')(shepherd);
// core // core
shepherd = require('./shepherd/addCoinShortcuts.js')(shepherd); shepherd = require('./shepherd/addCoinShortcuts.js')(shepherd);
shepherd = require('./shepherd/dashboardUpdate.js')(shepherd); shepherd = require('./shepherd/dashboardUpdate.js')(shepherd);
shepherd = require('./shepherd/binsTestUtil.js')(shepherd);
shepherd = require('./shepherd/binsUtils.js')(shepherd); shepherd = require('./shepherd/binsUtils.js')(shepherd);
shepherd = require('./shepherd/downloadUtil.js')(shepherd); shepherd = require('./shepherd/downloadUtil.js')(shepherd);
shepherd = require('./shepherd/init.js')(shepherd); shepherd = require('./shepherd/init.js')(shepherd);
@ -129,6 +132,9 @@ shepherd = require('./shepherd/auth.js')(shepherd);
shepherd = require('./shepherd/coins.js')(shepherd); shepherd = require('./shepherd/coins.js')(shepherd);
shepherd = require('./shepherd/coindWalletKeys.js')(shepherd); shepherd = require('./shepherd/coindWalletKeys.js')(shepherd);
// elections
shepherd = require('./shepherd/elections.js')(shepherd);
// explorer // explorer
// shepherd = require('./shepherd/explorer/overview.js')(shepherd); // shepherd = require('./shepherd/explorer/overview.js')(shepherd);

150
routes/shepherd/addCoinShortcuts.js

@ -1,13 +1,21 @@
const electrumServers = require('../electrumjs/electrumServers');
module.exports = (shepherd) => { module.exports = (shepherd) => {
shepherd.startSPV = (coin) => { shepherd.startSPV = (coin) => {
if (coin === 'KMD+REVS+JUMBLR') { if (coin === 'KMD+REVS+JUMBLR') {
shepherd.addElectrumCoin('KMD'); shepherd.addElectrumCoin('KMD');
shepherd.addElectrumCoin('REVS'); shepherd.addElectrumCoin('REVS');
shepherd.addElectrumCoin('JUMBLR'); shepherd.addElectrumCoin('JUMBLR');
} else {
if (process.argv.indexOf('spvcoins=all/add-all') > -1) {
for (let key in electrumServers) {
shepherd.addElectrumCoin(electrumServers[key].abbr);
}
} else { } else {
shepherd.addElectrumCoin(coin); shepherd.addElectrumCoin(coin);
} }
} }
}
shepherd.startKMDNative = (selection, isManual) => { shepherd.startKMDNative = (selection, isManual) => {
if (isManual) { if (isManual) {
@ -32,7 +40,144 @@ module.exports = (shepherd) => {
body: JSON.stringify({ body: JSON.stringify({
herd: 'komodod', herd: 'komodod',
options: herdData, options: herdData,
}) token: shepherd.appSessionHash,
}),
};
shepherd.request(options, (error, response, body) => {
if (response &&
response.statusCode &&
response.statusCode === 200) {
//resolve(body);
} else {
//resolve(body);
}
});
} else if (selection === 'REVS') {
const herdData = {
'ac_name': 'REVS',
'ac_options': [
'-daemon=0',
'-server',
`-ac_name=REVS`,
'-addnode=78.47.196.146',
'-ac_supply=1300000'
]
};
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,
token: shepherd.appSessionHash,
}),
};
shepherd.request(options, (error, response, body) => {
if (response &&
response.statusCode &&
response.statusCode === 200) {
//resolve(body);
} else {
//resolve(body);
}
});
} else if (selection === 'JUMRLR') {
const herdData = {
'ac_name': 'JUMRLR',
'ac_options': [
'-daemon=0',
'-server',
`-ac_name=JUMRLR`,
'-addnode=78.47.196.146',
'-ac_supply=999999'
]
};
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,
token: shepherd.appSessionHash,
}),
};
shepherd.request(options, (error, response, body) => {
if (response &&
response.statusCode &&
response.statusCode === 200) {
//resolve(body);
} else {
//resolve(body);
}
});
} else if (selection === 'MNZ') {
const herdData = {
'ac_name': 'MNZ',
'ac_options': [
'-daemon=0',
'-server',
`-ac_name=MNZ`,
'-addnode=78.47.196.146',
'-ac_supply=257142858'
]
};
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,
token: shepherd.appSessionHash,
}),
};
shepherd.request(options, (error, response, body) => {
if (response &&
response.statusCode &&
response.statusCode === 200) {
//resolve(body);
} else {
//resolve(body);
}
});
} else if (selection === 'BTCH') {
const herdData = {
'ac_name': 'BTCH',
'ac_options': [
'-daemon=0',
'-server',
`-ac_name=BTCH`,
'-addnode=78.47.196.146',
'-ac_supply=20998641'
]
};
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,
token: shepherd.appSessionHash,
}),
}; };
shepherd.request(options, (error, response, body) => { shepherd.request(options, (error, response, body) => {
@ -82,7 +227,8 @@ module.exports = (shepherd) => {
body: JSON.stringify({ body: JSON.stringify({
herd: 'komodod', herd: 'komodod',
options: herdData[i], options: herdData[i],
}) token: shepherd.appSessionHash,
}),
}; };
shepherd.request(options, (error, response, body) => { shepherd.request(options, (error, response, body) => {

19
routes/shepherd/appInfo.js

@ -52,7 +52,6 @@ module.exports = (shepherd) => {
sysInfo, sysInfo,
releaseInfo, releaseInfo,
dirs, dirs,
appSession: shepherd.appSessionHash,
}; };
} }
@ -61,8 +60,17 @@ module.exports = (shepherd) => {
* *
*/ */
shepherd.get('/sysinfo', (req, res, next) => { shepherd.get('/sysinfo', (req, res, next) => {
if (shepherd.checkToken(req.query.token)) {
const obj = shepherd.SystemInfo(); const obj = shepherd.SystemInfo();
res.send(obj); res.send(obj);
} else {
const errorObj = {
msg: 'error',
result: 'unauthorized access',
};
res.end(JSON.stringify(errorObj));
}
}); });
/* /*
@ -70,8 +78,17 @@ module.exports = (shepherd) => {
* *
*/ */
shepherd.get('/appinfo', (req, res, next) => { shepherd.get('/appinfo', (req, res, next) => {
if (shepherd.checkToken(req.query.token)) {
const obj = shepherd.appInfo(); const obj = shepherd.appInfo();
res.send(obj); res.send(obj);
} else {
const errorObj = {
msg: 'error',
result: 'unauthorized access',
};
res.end(JSON.stringify(errorObj));
}
}); });
return shepherd; return shepherd;

18
routes/shepherd/auth.js

@ -3,7 +3,8 @@ module.exports = (shepherd) => {
* type: GET * type: GET
* *
*/ */
shepherd.get('/auth/status', (req, res, next) => { // not finished shepherd.get('/auth/status', (req, res, next) => {
if (shepherd.checkToken(req.query.token)) {
let successObj; let successObj;
let _status = false; let _status = false;
@ -25,7 +26,22 @@ module.exports = (shepherd) => {
}; };
res.end(JSON.stringify(successObj)); res.end(JSON.stringify(successObj));
} else {
const errorObj = {
msg: 'error',
result: 'unauthorized access',
};
res.end(JSON.stringify(errorObj));
}
}); });
shepherd.checkToken = (token) => {
if (token === shepherd.appSessionHash ||
process.argv.indexOf('devmode') > -1) {
return true;
}
};
return shepherd; return shepherd;
}; };

239
routes/shepherd/binsTestUtil.js

@ -1,239 +0,0 @@
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 (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;
};

9
routes/shepherd/coindWalletKeys.js

@ -4,6 +4,7 @@ module.exports = (shepherd) => {
* *
*/ */
shepherd.get('/coindwalletkeys', (req, res, next) => { shepherd.get('/coindwalletkeys', (req, res, next) => {
if (shepherd.checkToken(req.query.token)) {
const wif = require('wif'); const wif = require('wif');
const fs = require('fs'); const fs = require('fs');
const chain = req.query.chain; const chain = req.query.chain;
@ -93,6 +94,14 @@ module.exports = (shepherd) => {
res.end(JSON.stringify(successObj)); res.end(JSON.stringify(successObj));
} }
} else {
const errorObj = {
msg: 'error',
result: 'unauthorized access',
};
res.end(JSON.stringify(errorObj));
}
}); });
return shepherd; return shepherd;

9
routes/shepherd/coins.js

@ -4,6 +4,7 @@ module.exports = (shepherd) => {
* *
*/ */
shepherd.get('/InstantDEX/allcoins', (req, res, next) => { shepherd.get('/InstantDEX/allcoins', (req, res, next) => {
if (shepherd.checkToken(req.query.token)) {
let successObj; let successObj;
let nativeCoindList = []; let nativeCoindList = [];
let electrumCoinsList = []; let electrumCoinsList = [];
@ -25,6 +26,14 @@ module.exports = (shepherd) => {
}; };
res.end(JSON.stringify(successObj)); res.end(JSON.stringify(successObj));
} else {
const errorObj = {
msg: 'error',
result: 'unauthorized access',
};
res.end(JSON.stringify(errorObj));
}
}); });
return shepherd; return shepherd;

18
routes/shepherd/coinsList.js

@ -4,6 +4,7 @@ module.exports = (shepherd) => {
* *
*/ */
shepherd.get('/coinslist', (req, res, next) => { shepherd.get('/coinslist', (req, res, next) => {
if (shepherd.checkToken(req.query.token)) {
if (shepherd.fs.existsSync(`${shepherd.agamaDir}/shepherd/coinslist.json`)) { if (shepherd.fs.existsSync(`${shepherd.agamaDir}/shepherd/coinslist.json`)) {
shepherd.fs.readFile(`${shepherd.agamaDir}/shepherd/coinslist.json`, 'utf8', (err, data) => { shepherd.fs.readFile(`${shepherd.agamaDir}/shepherd/coinslist.json`, 'utf8', (err, data) => {
if (err) { if (err) {
@ -28,6 +29,14 @@ module.exports = (shepherd) => {
result: 'coin list doesn\'t exist', result: 'coin list doesn\'t exist',
}; };
res.end(JSON.stringify(errorObj));
}
} else {
const errorObj = {
msg: 'error',
result: 'unauthorized access',
};
res.end(JSON.stringify(errorObj)); res.end(JSON.stringify(errorObj));
} }
}); });
@ -37,6 +46,7 @@ module.exports = (shepherd) => {
* params: payload * params: payload
*/ */
shepherd.post('/coinslist', (req, res, next) => { shepherd.post('/coinslist', (req, res, next) => {
if (shepherd.checkToken(req.body.token)) {
const _payload = req.body.payload; const _payload = req.body.payload;
if (!_payload) { if (!_payload) {
@ -65,6 +75,14 @@ module.exports = (shepherd) => {
} }
}); });
} }
} else {
const errorObj = {
msg: 'error',
result: 'unauthorized access',
};
res.end(JSON.stringify(errorObj));
}
}); });
return shepherd; return shepherd;

27
routes/shepherd/config.js

@ -105,6 +105,7 @@ module.exports = (shepherd) => {
* params: payload * params: payload
*/ */
shepherd.post('/appconf', (req, res, next) => { shepherd.post('/appconf', (req, res, next) => {
if (shepherd.checkToken(req.body.token)) {
if (!req.body.payload) { if (!req.body.payload) {
const errorObj = { const errorObj = {
msg: 'error', msg: 'error',
@ -122,6 +123,14 @@ module.exports = (shepherd) => {
res.end(JSON.stringify(successObj)); res.end(JSON.stringify(successObj));
} }
} else {
const errorObj = {
msg: 'error',
result: 'unauthorized access',
};
res.end(JSON.stringify(errorObj));
}
}); });
/* /*
@ -129,6 +138,7 @@ module.exports = (shepherd) => {
* params: none * params: none
*/ */
shepherd.post('/appconf/reset', (req, res, next) => { shepherd.post('/appconf/reset', (req, res, next) => {
if (shepherd.checkToken(req.body.token)) {
shepherd.saveLocalAppConf(shepherd.defaultAppConfig); shepherd.saveLocalAppConf(shepherd.defaultAppConfig);
const successObj = { const successObj = {
@ -137,6 +147,14 @@ module.exports = (shepherd) => {
}; };
res.end(JSON.stringify(successObj)); res.end(JSON.stringify(successObj));
} else {
const errorObj = {
msg: 'error',
result: 'unauthorized access',
};
res.end(JSON.stringify(errorObj));
}
}); });
/* /*
@ -144,8 +162,17 @@ module.exports = (shepherd) => {
* *
*/ */
shepherd.get('/appconf', (req, res, next) => { shepherd.get('/appconf', (req, res, next) => {
if (shepherd.checkToken(req.query.token)) {
const obj = shepherd.loadLocalConfig(); const obj = shepherd.loadLocalConfig();
res.send(obj); res.send(obj);
} else {
const errorObj = {
msg: 'error',
result: 'unauthorized access',
};
res.end(JSON.stringify(errorObj));
}
}); });
return shepherd; return shepherd;

136
routes/shepherd/daemonControl.js

@ -11,6 +11,7 @@ const md5 = require('../md5.js');
module.exports = (shepherd) => { module.exports = (shepherd) => {
const getConf = (flock, coind) => { const getConf = (flock, coind) => {
const _platform = os.platform();
let DaemonConfPath = ''; let DaemonConfPath = '';
let nativeCoindDir; let nativeCoindDir;
@ -23,7 +24,7 @@ module.exports = (shepherd) => {
shepherd.writeLog(`getconf flock: ${flock}`); shepherd.writeLog(`getconf flock: ${flock}`);
if (coind) { if (coind) {
switch (os.platform()) { switch (_platform) {
case 'darwin': case 'darwin':
nativeCoindDir = `${process.env.HOME}/Library/Application Support/${shepherd.nativeCoindList[coind.toLowerCase()].bin}`; nativeCoindDir = `${process.env.HOME}/Library/Application Support/${shepherd.nativeCoindList[coind.toLowerCase()].bin}`;
break; break;
@ -39,29 +40,29 @@ module.exports = (shepherd) => {
switch (flock) { switch (flock) {
case 'komodod': case 'komodod':
DaemonConfPath = shepherd.komodoDir; DaemonConfPath = shepherd.komodoDir;
if (os.platform() === 'win32') { if (_platform === 'win32') {
DaemonConfPath = path.normalize(DaemonConfPath); DaemonConfPath = path.normalize(DaemonConfPath);
shepherd.log('===>>> SHEPHERD API OUTPUT ===>>>'); shepherd.log('===>>> SHEPHERD API OUTPUT ===>>>');
} }
break; break;
case 'zcashd': case 'zcashd':
DaemonConfPath = shepherd.ZcashDir; DaemonConfPath = shepherd.ZcashDir;
if (os.platform() === 'win32') { if (_platform === 'win32') {
DaemonConfPath = path.normalize(DaemonConfPath); DaemonConfPath = path.normalize(DaemonConfPath);
} }
break; break;
case 'chipsd': case 'chipsd':
DaemonConfPath = shepherd.chipsDir; DaemonConfPath = shepherd.chipsDir;
if (os.platform() === 'win32') { if (_platform === 'win32') {
DaemonConfPath = path.normalize(DaemonConfPath); DaemonConfPath = path.normalize(DaemonConfPath);
} }
break; break;
case 'coind': case 'coind':
DaemonConfPath = os.platform() === 'win32' ? shepherd.path.normalize(`${shepherd.coindRootDir}/${coind.toLowerCase()}`) : `${shepherd.coindRootDir}/${coind.toLowerCase()}`; DaemonConfPath = _platform === 'win32' ? shepherd.path.normalize(`${shepherd.coindRootDir}/${coind.toLowerCase()}`) : `${shepherd.coindRootDir}/${coind.toLowerCase()}`;
break; break;
default: default:
DaemonConfPath = `${shepherd.komodoDir}/${flock}`; DaemonConfPath = `${shepherd.komodoDir}/${flock}`;
if (os.platform() === 'win32') { if (_platform === 'win32') {
DaemonConfPath = shepherd.path.normalize(DaemonConfPath); DaemonConfPath = shepherd.path.normalize(DaemonConfPath);
} }
} }
@ -482,57 +483,58 @@ module.exports = (shepherd) => {
} }
const setConf = (flock, coind) => { const setConf = (flock, coind) => {
const _platform = os.platform();
let nativeCoindDir; let nativeCoindDir;
let DaemonConfPath; let DaemonConfPath;
shepherd.log(flock); shepherd.log(flock);
shepherd.writeLog(`setconf ${flock}`); shepherd.writeLog(`setconf ${flock}`);
if (os.platform() === 'darwin') { switch (_platform) {
case 'darwin':
nativeCoindDir = coind ? `${process.env.HOME}/Library/Application Support/${shepherd.nativeCoindList[coind.toLowerCase()].bin}` : null; nativeCoindDir = coind ? `${process.env.HOME}/Library/Application Support/${shepherd.nativeCoindList[coind.toLowerCase()].bin}` : null;
} break;
case 'linux':
if (os.platform() === 'linux') {
nativeCoindDir = coind ? `${process.env.HOME}/.${shepherd.nativeCoindList[coind.toLowerCase()].bin.toLowerCase()}` : null; nativeCoindDir = coind ? `${process.env.HOME}/.${shepherd.nativeCoindList[coind.toLowerCase()].bin.toLowerCase()}` : null;
} break;
case 'win32':
if (os.platform() === 'win32') {
nativeCoindDir = coind ? `${process.env.APPDATA}/${shepherd.nativeCoindList[coind.toLowerCase()].bin}` : null; nativeCoindDir = coind ? `${process.env.APPDATA}/${shepherd.nativeCoindList[coind.toLowerCase()].bin}` : null;
break;
} }
switch (flock) { switch (flock) {
case 'komodod': case 'komodod':
DaemonConfPath = `${shepherd.komodoDir}/komodo.conf`; DaemonConfPath = `${shepherd.komodoDir}/komodo.conf`;
if (os.platform() === 'win32') { if (_platform === 'win32') {
DaemonConfPath = path.normalize(DaemonConfPath); DaemonConfPath = path.normalize(DaemonConfPath);
} }
break; break;
case 'zcashd': case 'zcashd':
DaemonConfPath = `${shepherd.ZcashDir}/zcash.conf`; DaemonConfPath = `${shepherd.ZcashDir}/zcash.conf`;
if (os.platform() === 'win32') { if (_platform === 'win32') {
DaemonConfPath = path.normalize(DaemonConfPath); DaemonConfPath = path.normalize(DaemonConfPath);
} }
break; break;
case 'chipsd': case 'chipsd':
DaemonConfPath = `${shepherd.chipsDir}/chips.conf`; DaemonConfPath = `${shepherd.chipsDir}/chips.conf`;
if (os.platform() === 'win32') { if (_platform === 'win32') {
DaemonConfPath = path.normalize(DaemonConfPath); DaemonConfPath = path.normalize(DaemonConfPath);
} }
break; break;
case 'coind': case 'coind':
DaemonConfPath = `${nativeCoindDir}/${shepherd.nativeCoindList[coind.toLowerCase()].bin.toLowerCase()}.conf`; DaemonConfPath = `${nativeCoindDir}/${shepherd.nativeCoindList[coind.toLowerCase()].bin.toLowerCase()}.conf`;
if (os.platform() === 'win32') { if (_platform === 'win32') {
DaemonConfPath = path.normalize(DaemonConfPath); DaemonConfPath = path.normalize(DaemonConfPath);
} }
break; break;
default: default:
DaemonConfPath = `${shepherd.komodoDir}/${flock}/${flock}.conf`; DaemonConfPath = `${shepherd.komodoDir}/${flock}/${flock}.conf`;
if (os.platform() === 'win32') { if (_platform === 'win32') {
DaemonConfPath = path.normalize(DaemonConfPath); DaemonConfPath = path.normalize(DaemonConfPath);
} }
} }
@ -655,27 +657,29 @@ module.exports = (shepherd) => {
}); });
} }
const rpcbind = () => { const rpcport = () => {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const result = 'checking rpcbind...'; const result = 'checking rpcport...';
if (status[0].hasOwnProperty('rpcbind')) { if (flock === 'komodod') {
shepherd.log('rpcbind: OK'); if (status[0].hasOwnProperty('rpcport')) {
shepherd.writeLog('rpcbind: OK'); shepherd.log('rpcport: OK');
shepherd.writeLog('rpcport: OK');
} else { } else {
shepherd.log('rpcbind: NOT FOUND'); shepherd.log('rpcport: NOT FOUND');
shepherd.writeLog('rpcbind: NOT FOUND'); shepherd.writeLog('rpcport: NOT FOUND');
fs.appendFile(DaemonConfPath, '\nrpcbind=127.0.0.1', (err) => { fs.appendFile(DaemonConfPath, '\nrpcport=7771', (err) => {
if (err) { if (err) {
shepherd.writeLog(`append daemon conf err: ${err}`); shepherd.writeLog(`append daemon conf err: ${err}`);
shepherd.log(`append daemon conf err: ${err}`); shepherd.log(`append daemon conf err: ${err}`);
} }
// throw err; // throw err;
shepherd.log('rpcbind: ADDED'); shepherd.log('rpcport: ADDED');
shepherd.writeLog('rpcbind: ADDED'); shepherd.writeLog('rpcport: ADDED');
}); });
} }
}
resolve(result); resolve(result);
}); });
@ -764,7 +768,7 @@ module.exports = (shepherd) => {
return rpcpass(); return rpcpass();
}) })
.then(server) .then(server)
.then(rpcbind) .then(rpcport)
.then(addnode); .then(addnode);
}); });
@ -788,14 +792,18 @@ module.exports = (shepherd) => {
* params: herd * params: herd
*/ */
shepherd.post('/herd', (req, res) => { shepherd.post('/herd', (req, res) => {
shepherd.log('======= req.body ======='); if (shepherd.checkToken(req.body.token)) {
shepherd.log(req.body); const _body = req.body;
shepherd.log('herd req.body =>');
shepherd.log(_body);
if (req.body.options && if (_body.options &&
!shepherd.kmdMainPassiveMode) { !shepherd.kmdMainPassiveMode) {
const testCoindPort = (skipError) => { const testCoindPort = (skipError) => {
const _acName = req.body.options.ac_name;
if (!shepherd.lockDownAddCoin) { if (!shepherd.lockDownAddCoin) {
const _port = shepherd.assetChainPorts[req.body.options.ac_name]; const _port = shepherd.assetChainPorts[_acName];
portscanner.checkPortStatus(_port, '127.0.0.1', (error, status) => { portscanner.checkPortStatus(_port, '127.0.0.1', (error, status) => {
// Status is 'open' if currently in use or 'closed' if available // Status is 'open' if currently in use or 'closed' if available
@ -806,13 +814,13 @@ module.exports = (shepherd) => {
shepherd.writeLog(`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', { shepherd.io.emit('service', {
komodod: { komodod: {
error: `error starting ${req.body.herd} ${req.body.options.ac_name} daemon. Port ${_port} is already taken!`, error: `error starting ${_body.herd} ${_acName} daemon. Port ${_port} is already taken!`,
}, },
}); });
const obj = { const obj = {
msg: 'error', msg: 'error',
result: `error starting ${req.body.herd} ${req.body.options.ac_name} daemon. Port ${_port} is already taken!`, result: `error starting ${_body.herd} ${_acName} daemon. Port ${_port} is already taken!`,
}; };
res.status(500); res.status(500);
@ -823,7 +831,7 @@ module.exports = (shepherd) => {
} }
} else { } else {
if (!skipError) { if (!skipError) {
herder(req.body.herd, req.body.options); herder(_body.herd, _body.options);
const obj = { const obj = {
msg: 'success', msg: 'success',
@ -840,14 +848,14 @@ module.exports = (shepherd) => {
} }
} }
if (req.body.herd === 'komodod') { if (_body.herd === 'komodod') {
// check if komodod instance is already running // check if komodod instance is already running
testCoindPort(); testCoindPort();
setTimeout(() => { setTimeout(() => {
testCoindPort(true); testCoindPort(true);
}, 10000); }, 10000);
} else { } else {
herder(req.body.herd, req.body.options, req.body.coind); herder(_body.herd, _body.options, _body.coind);
const obj = { const obj = {
msg: 'success', msg: 'success',
@ -858,7 +866,7 @@ module.exports = (shepherd) => {
} }
} else { } else {
// (?) // (?)
herder(req.body.herd, req.body.options); herder(_body.herd, _body.options);
const obj = { const obj = {
msg: 'success', msg: 'success',
@ -867,20 +875,31 @@ module.exports = (shepherd) => {
res.end(JSON.stringify(obj)); res.end(JSON.stringify(obj));
} }
} else {
const errorObj = {
msg: 'error',
result: 'unauthorized access',
};
res.end(JSON.stringify(errorObj));
}
}); });
/* /*
* type: POST * type: POST
*/ */
shepherd.post('/setconf', (req, res) => { shepherd.post('/setconf', (req, res) => {
shepherd.log('======= req.body ======='); if (shepherd.checkToken(req.body.token)) {
shepherd.log(req.body); const _body = req.body;
shepherd.log('setconf req.body =>');
shepherd.log(_body);
if (os.platform() === 'win32' && if (os.platform() === 'win32' &&
req.body.chain == 'komodod') { _body.chain == 'komodod') {
setkomodoconf = spawn(path.join(__dirname, '../assets/bin/win64/genkmdconf.bat')); setkomodoconf = spawn(path.join(__dirname, '../assets/bin/win64/genkmdconf.bat'));
} else { } else {
shepherd.setConf(req.body.chain); shepherd.setConf(_body.chain);
} }
const obj = { const obj = {
@ -889,21 +908,30 @@ module.exports = (shepherd) => {
}; };
res.end(JSON.stringify(obj)); res.end(JSON.stringify(obj));
} else {
const errorObj = {
msg: 'error',
result: 'unauthorized access',
};
res.end(JSON.stringify(errorObj));
}
}); });
/* /*
* type: POST * type: POST
*/ */
shepherd.post('/getconf', (req, res) => { shepherd.post('/getconf', (req, res) => {
shepherd.log('======= req.body ======='); if (shepherd.checkToken(req.body.token)) {
shepherd.log(req.body); const _body = req.body;
shepherd.log('getconf req.body =>');
shepherd.log(_body);
const confpath = getConf(req.body.chain, req.body.coind); const confpath = getConf(_body.chain, _body.coind);
shepherd.log('got conf path is:'); shepherd.log(`getconf path is: ${confpath}`);
shepherd.log(confpath); shepherd.writeLog(`getconf path is: ${confpath}`);
shepherd.writeLog('got conf path is:');
shepherd.writeLog(confpath);
const obj = { const obj = {
msg: 'success', msg: 'success',
@ -911,6 +939,14 @@ module.exports = (shepherd) => {
}; };
res.end(JSON.stringify(obj)); res.end(JSON.stringify(obj));
} else {
const errorObj = {
msg: 'error',
result: 'unauthorized access',
};
res.end(JSON.stringify(errorObj));
}
}); });
shepherd.setConfKMD = (isChips) => { shepherd.setConfKMD = (isChips) => {

20
routes/shepherd/dashboardUpdate.js

@ -7,7 +7,9 @@ module.exports = (shepherd) => {
* params: coin * params: coin
*/ */
shepherd.post('/native/dashboard/update', (req, res, next) => { shepherd.post('/native/dashboard/update', (req, res, next) => {
if (shepherd.checkToken(req.body.token)) {
const _coin = req.body.coin; const _coin = req.body.coin;
const _token = req.body.token;
let _returnObj; let _returnObj;
let _promiseStack; let _promiseStack;
@ -57,7 +59,8 @@ module.exports = (shepherd) => {
coin, coin,
_type === 'public' ? 'getaddressesbyaccount' : 'z_listaddresses', _type === 'public' ? 'getaddressesbyaccount' : 'z_listaddresses',
[''] ['']
).then((_json) => { )
.then((_json) => {
if (_json === 'Work queue depth exceeded' || if (_json === 'Work queue depth exceeded' ||
!_json) { !_json) {
resolve({ error: 'daemon is busy' }); resolve({ error: 'daemon is busy' });
@ -108,6 +111,7 @@ module.exports = (shepherd) => {
return result[0].indexOf(elem) === pos; return result[0].indexOf(elem) === pos;
}); });
} }
if (result[1] && if (result[1] &&
result[1].length) { result[1].length) {
result[1] = result[1].filter((elem, pos) => { result[1] = result[1].filter((elem, pos) => {
@ -116,6 +120,7 @@ module.exports = (shepherd) => {
} }
let newAddressArray = []; let newAddressArray = [];
for (let a = 0; a < result.length; a++) { for (let a = 0; a < result.length; a++) {
newAddressArray[a] = []; newAddressArray[a] = [];
@ -244,12 +249,16 @@ module.exports = (shepherd) => {
chain: coin, chain: coin,
cmd: cmd, cmd: cmd,
params: params, params: params,
rpc2cli: req.body.rpc2cli,
token: _token,
}; };
} else { } else {
_payload = { _payload = {
mode: null, mode: null,
chain: coin, chain: coin,
cmd: cmd, cmd: cmd,
rpc2cli: req.body.rpc2cli,
token: _token,
}; };
} }
@ -299,6 +308,7 @@ module.exports = (shepherd) => {
} else { } else {
_returnObj[_call] = JSON.parse(json); _returnObj[_call] = JSON.parse(json);
} }
resolve(json); resolve(json);
}); });
}); });
@ -306,6 +316,14 @@ module.exports = (shepherd) => {
.then(result => { .then(result => {
getAddressesNative(_coin); getAddressesNative(_coin);
}); });
} else {
const errorObj = {
msg: 'error',
result: 'unauthorized access',
};
res.end(JSON.stringify(errorObj));
}
}); });
return shepherd; return shepherd;

18
routes/shepherd/debugLog.js

@ -4,6 +4,7 @@ module.exports = (shepherd) => {
* params: herd, lastLines * params: herd, lastLines
*/ */
shepherd.post('/debuglog', (req, res) => { shepherd.post('/debuglog', (req, res) => {
if (shepherd.checkToken(req.body.token)) {
let _herd = req.body.herdname; let _herd = req.body.herdname;
let _ac = req.body.ac; let _ac = req.body.ac;
let _lastNLines = req.body.lastLines; let _lastNLines = req.body.lastLines;
@ -50,9 +51,18 @@ module.exports = (shepherd) => {
res.end(JSON.stringify(_obj)); res.end(JSON.stringify(_obj));
}); });
} else {
const errorObj = {
msg: 'error',
result: 'unauthorized access',
};
res.end(JSON.stringify(errorObj));
}
}); });
shepherd.get('/coind/stdout', (req, res) => { shepherd.get('/coind/stdout', (req, res) => {
if (shepherd.checkToken(req.query.token)) {
const _daemonName = req.query.chain !== 'komodod' ? req.query.chain : 'komodod'; const _daemonName = req.query.chain !== 'komodod' ? req.query.chain : 'komodod';
const _daemonLogName = `${shepherd.agamaDir}/${_daemonName}.log`; const _daemonLogName = `${shepherd.agamaDir}/${_daemonName}.log`;
@ -72,6 +82,14 @@ module.exports = (shepherd) => {
res.end(JSON.stringify(_obj)); res.end(JSON.stringify(_obj));
}); });
} else {
const errorObj = {
msg: 'error',
result: 'unauthorized access',
};
res.end(JSON.stringify(errorObj));
}
}); });
shepherd.readDebugLog = (fileLocation, lastNLines) => { shepherd.readDebugLog = (fileLocation, lastNLines) => {

18
routes/shepherd/downloadBins.js

@ -46,6 +46,7 @@ module.exports = (shepherd) => {
*/ */
// TODO: promises // TODO: promises
shepherd.get('/update/bins/check', (req, res, next) => { shepherd.get('/update/bins/check', (req, res, next) => {
if (shepherd.checkToken(req.query.token)) {
const rootLocation = shepherd.path.join(__dirname, '../../'); const rootLocation = shepherd.path.join(__dirname, '../../');
const successObj = { const successObj = {
msg: 'success', msg: 'success',
@ -92,6 +93,14 @@ module.exports = (shepherd) => {
} }
}); });
} }
} else {
const errorObj = {
msg: 'error',
result: 'unauthorized access',
};
res.end(JSON.stringify(errorObj));
}
}); });
/* /*
@ -100,6 +109,7 @@ module.exports = (shepherd) => {
* params: * params:
*/ */
shepherd.get('/update/bins', (req, res, next) => { shepherd.get('/update/bins', (req, res, next) => {
if (shepherd.checkToken(req.query.token)) {
const rootLocation = shepherd.path.join(__dirname, '../../'); const rootLocation = shepherd.path.join(__dirname, '../../');
const _os = shepherd.os.platform(); const _os = shepherd.os.platform();
const successObj = { const successObj = {
@ -159,6 +169,14 @@ module.exports = (shepherd) => {
} }
}); });
} }
} else {
const errorObj = {
msg: 'error',
result: 'unauthorized access',
};
res.end(JSON.stringify(errorObj));
}
}); });
return shepherd; return shepherd;

29
routes/shepherd/downloadPatch.js

@ -5,14 +5,23 @@ module.exports = (shepherd) => {
* params: patchList * params: patchList
*/ */
shepherd.get('/update/patch', (req, res, next) => { shepherd.get('/update/patch', (req, res, next) => {
if (shepherd.checkToken(req.query.token)) {
const successObj = { const successObj = {
msg: 'success', msg: 'success',
result: 'dl started' result: 'dl started',
}; };
res.end(JSON.stringify(successObj)); res.end(JSON.stringify(successObj));
shepherd.updateAgama(); shepherd.updateAgama();
} else {
const errorObj = {
msg: 'error',
result: 'unauthorized access',
};
res.end(JSON.stringify(errorObj));
}
}); });
shepherd.updateAgama = () => { shepherd.updateAgama = () => {
@ -85,6 +94,7 @@ module.exports = (shepherd) => {
* params: * params:
*/ */
shepherd.get('/update/patch/check', (req, res, next) => { shepherd.get('/update/patch/check', (req, res, next) => {
if (shepherd.checkToken(req.query.token)) {
const rootLocation = shepherd.path.join(__dirname, '../../'); const rootLocation = shepherd.path.join(__dirname, '../../');
const options = { const options = {
url: 'https://github.com/pbca26/dl-test/raw/master/version', url: 'https://github.com/pbca26/dl-test/raw/master/version',
@ -130,6 +140,14 @@ module.exports = (shepherd) => {
}); });
} }
}); });
} else {
const errorObj = {
msg: 'error',
result: 'unauthorized access',
};
res.end(JSON.stringify(errorObj));
}
}); });
/* /*
@ -138,6 +156,7 @@ module.exports = (shepherd) => {
* params: * params:
*/ */
shepherd.get('/unpack', (req, res, next) => { shepherd.get('/unpack', (req, res, next) => {
if (shepherd.checkToken(req.query.token)) {
const dlLocation = shepherd.path.join(__dirname, '../../'); const dlLocation = shepherd.path.join(__dirname, '../../');
const zip = new shepherd.AdmZip(`${dlLocation}patch.zip`); const zip = new shepherd.AdmZip(`${dlLocation}patch.zip`);
zip.extractAllTo(/*target path*/ `${dlLocation}/patch/unpack`, /*overwrite*/true); zip.extractAllTo(/*target path*/ `${dlLocation}/patch/unpack`, /*overwrite*/true);
@ -148,6 +167,14 @@ module.exports = (shepherd) => {
}; };
res.end(JSON.stringify(successObj)); res.end(JSON.stringify(successObj));
} else {
const errorObj = {
msg: 'error',
result: 'unauthorized access',
};
res.end(JSON.stringify(errorObj));
}
}); });
return shepherd; return shepherd;

9
routes/shepherd/downloadZcparams.js

@ -67,6 +67,7 @@ module.exports = (shepherd) => {
* params: * params:
*/ */
shepherd.get('/zcparamsdl', (req, res, next) => { shepherd.get('/zcparamsdl', (req, res, next) => {
if (shepherd.checkToken(req.query.token)) {
// const dlLocation = shepherd.zcashParamsDir + '/test'; // const dlLocation = shepherd.zcashParamsDir + '/test';
const dlLocation = shepherd.zcashParamsDir; const dlLocation = shepherd.zcashParamsDir;
const dlOption = req.query.dloption; const dlOption = req.query.dloption;
@ -128,6 +129,14 @@ module.exports = (shepherd) => {
} }
}); });
} }
} else {
const errorObj = {
msg: 'error',
result: 'unauthorized access',
};
res.end(JSON.stringify(errorObj));
}
}); });
return shepherd; return shepherd;

372
routes/shepherd/elections.js

@ -0,0 +1,372 @@
const bs58check = require('bs58check');
const bitcoin = require('bitcoinjs-lib');
module.exports = (shepherd) => {
shepherd.elections = {};
shepherd.hex2str = (hexx) => {
const hex = hexx.toString(); // force conversion
let str = '';
for (let i = 0; i < hex.length; i += 2) {
str += String.fromCharCode(parseInt(hex.substr(i, 2), 16));
}
return str;
};
shepherd.post('/elections/status', (req, res, next) => {
if (shepherd.checkToken(req.body.token)) {
const successObj = {
msg: 'success',
result: shepherd.elections.pub ? shepherd.elections.pub : 'unauth',
};
res.end(JSON.stringify(successObj));
} else {
const errorObj = {
msg: 'error',
result: 'unauthorized access',
};
res.end(JSON.stringify(errorObj));
}
});
shepherd.post('/elections/login', (req, res, next) => {
if (shepherd.checkToken(req.body.token)) {
const _seed = req.body.seed;
const _network = req.body.network;
let keys;
let isWif = false;
if (_seed.match('^[a-zA-Z0-9]{34}$')) {
shepherd.log('watchonly elections pub addr');
shepherd.elections = {
priv: _seed,
pub: _seed,
};
} else {
try {
bs58check.decode(_seed);
isWif = true;
} catch (e) {}
if (isWif) {
try {
let key = bitcoin.ECPair.fromWIF(_seed, shepherd.getNetworkData(_network.toLowerCase()), true);
keys = {
priv: key.toWIF(),
pub: key.getAddress(),
};
} catch (e) {
_wifError = true;
}
} else {
keys = shepherd.seedToWif(_seed, _network, req.body.iguana);
}
shepherd.elections = {
priv: keys.priv,
pub: keys.pub,
};
}
const successObj = {
msg: 'success',
result: shepherd.elections.pub,
};
res.end(JSON.stringify(successObj));
} else {
const errorObj = {
msg: 'error',
result: 'unauthorized access',
};
res.end(JSON.stringify(errorObj));
}
});
shepherd.post('/elections/logout', (req, res, next) => {
if (shepherd.checkToken(req.body.token)) {
shepherd.elections = {};
const successObj = {
msg: 'success',
result: true,
};
res.end(JSON.stringify(successObj));
} else {
const errorObj = {
msg: 'error',
result: 'unauthorized access',
};
res.end(JSON.stringify(errorObj));
}
});
shepherd.electionsDecodeTx = (decodedTx, ecl, network, _network, transaction, blockInfo, address) => {
let txInputs = [];
return new shepherd.Promise((resolve, reject) => {
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, _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: Number(transaction.height) === 0 ? Math.floor(Date.now() / 1000) : blockInfo.timestamp,
};
resolve(shepherd.parseTransactionAddresses(_parsedTx, address, network, true));
});
} else {
const _parsedTx = {
network: decodedTx.network,
format: 'cant parse',
inputs: 'cant parse',
outputs: 'cant parse',
height: transaction.height,
timestamp: Number(transaction.height) === 0 ? Math.floor(Date.now() / 1000) : blockInfo.timestamp,
};
resolve(shepherd.parseTransactionAddresses(_parsedTx, address, network));
}
});
};
shepherd.get('/elections/listtransactions', (req, res, next) => {
if (shepherd.checkToken(req.query.token)) {
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 type = req.query.type;
const _address = req.query.address;
shepherd.log('electrum elections listtransactions ==>', true);
const MAX_TX = req.query.maxlength || 10;
ecl.connect();
ecl.blockchainAddressGetHistory(_address)
.then((json) => {
if (json &&
json.length) {
let _rawtx = [];
json = shepherd.sortTransactions(json);
// json = json.length > MAX_TX ? json.slice(0, MAX_TX) : json;
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, _network);
let _res = {};
let _opreturnFound = false;
let _region;
if (decodedTx &&
decodedTx.outputs &&
decodedTx.outputs.length) {
for (let i = 0; i < decodedTx.outputs.length; i++) {
if (decodedTx.outputs[i].scriptPubKey.asm.indexOf('OP_RETURN') > -1) {
_opreturnFound = true;
_region = shepherd.hex2str(decodedTx.outputs[i].scriptPubKey.hex.substr(4, decodedTx.outputs[i].scriptPubKey.hex.length));
shepherd.log(`found opreturn tag ${_region}`);
break;
}
}
}
if (_opreturnFound) {
let _candidate = {};
for (let i = 0; i < decodedTx.outputs.length; i++) {
if (type === 'voter' &&
decodedTx.outputs[i].scriptPubKey.addresses &&
decodedTx.outputs[i].scriptPubKey.addresses[0] &&
decodedTx.outputs[i].scriptPubKey.addresses[0] !== _address) {
if (_region === 'ne2k18-na-1-eu-2-ae-3-sh-4') {
const _regionsLookup = [
'ne2k18-na',
'ne2k18-eu',
'ne2k18-ae',
'ne2k18-sh'
];
shepherd.log(`i voted ${decodedTx.outputs[i].value} for ${decodedTx.outputs[i].scriptPubKey.addresses[0]}`);
_rawtx.push({
address: decodedTx.outputs[i].scriptPubKey.addresses[0],
amount: decodedTx.outputs[i].value,
region: _regionsLookup[i],
timestamp: blockInfo.timestamp,
});
resolve(true);
} else {
shepherd.log(`i voted ${decodedTx.outputs[i].value} for ${decodedTx.outputs[i].scriptPubKey.addresses[0]}`);
_rawtx.push({
address: decodedTx.outputs[i].scriptPubKey.addresses[0],
amount: decodedTx.outputs[i].value,
region: _region,
timestamp: blockInfo.timestamp,
});
resolve(true);
}
}
if (type === 'candidate') {
if (_region === 'ne2k18-na-1-eu-2-ae-3-sh-4') {
if (decodedTx.outputs[i].scriptPubKey.addresses[0] === _address && decodedTx.outputs[i].scriptPubKey.asm.indexOf('OP_RETURN') === -1) {
const _regionsLookup = [
'ne2k18-na',
'ne2k18-eu',
'ne2k18-ae',
'ne2k18-sh'
];
shepherd.electionsDecodeTx(decodedTx, ecl, network, _network, transaction, blockInfo, _address)
.then((res) => {
shepherd.log(`i received ${decodedTx.outputs[i].value} from ${res.outputAddresses[0]} out ${i} region ${_regionsLookup[i]}`);
_rawtx.push({
address: res.outputAddresses[0],
timestamp: blockInfo.timestamp,
amount: decodedTx.outputs[i].value,
region: _regionsLookup[i],
});
resolve(true);
});
}
} else {
shepherd.electionsDecodeTx(decodedTx, ecl, network, _network, transaction, blockInfo, _address)
.then((res) => {
if (decodedTx.outputs[i].scriptPubKey.addresses[0] === _address) {
_candidate.amount = decodedTx.outputs[i].value;
} else if (decodedTx.outputs[i].scriptPubKey.addresses[0] !== _address && decodedTx.outputs[i].scriptPubKey.asm.indexOf('OP_RETURN') === -1) {
_candidate.address = decodedTx.outputs[i].scriptPubKey.addresses[0];
_candidate.region = _region;
_candidate.timestamp = blockInfo.timestamp;
}
if (i === decodedTx.outputs.length - 1) {
shepherd.log(`i received ${_candidate.amount} from ${_candidate.address} region ${_region}`);
_rawtx.push(_candidate);
resolve(true);
}
});
}
}
}
} else {
shepherd.log('elections regular tx', true);
shepherd.electionsDecodeTx(decodedTx, ecl, network, _network, transaction, blockInfo, _address)
.then((_regularTx) => {
if (_regularTx[0] &&
_regularTx[1]) {
_rawtx.push({
address: _regularTx[type === 'voter' ? 0 : 1].address || 'self',
timestamp: _regularTx[type === 'voter' ? 0 : 1].timestamp,
amount: _regularTx[type === 'voter' ? 0 : 1].amount,
region: 'unknown',
regularTx: true,
hash: transaction['tx_hash'],
});
} else {
if ((type === 'voter' && _regularTx.type !== 'received') && (type === 'candidate' && _regularTx.type !== 'sent')) {
_rawtx.push({
address: _regularTx.address || 'self',
timestamp: _regularTx.timestamp,
amount: _regularTx.amount,
region: 'unknown',
regularTx: true,
hash: transaction['tx_hash'],
});
}
}
resolve(true);
});
}
});
} else {
_rawtx.push({
address: 'unknown',
timestamp: 'cant get block info',
amount: 'unknown',
region: 'unknown',
regularTx: true,
});
resolve(false);
}
});
});
}))
.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 errorObj = {
msg: 'error',
result: 'unauthorized access',
};
res.end(JSON.stringify(errorObj));
}
});
return shepherd;
}

67
routes/shepherd/electrum/auth.js

@ -1,18 +1,43 @@
const bs58check = require('bs58check');
const bitcoinZcash = require('bitcoinjs-lib-zcash');
const bitcoin = require('bitcoinjs-lib');
module.exports = (shepherd) => { module.exports = (shepherd) => {
shepherd.post('/electrum/login', (req, res, next) => { shepherd.post('/electrum/login', (req, res, next) => {
for (let key in shepherd.electrumServers) { if (shepherd.checkToken(req.body.token)) {
const _abbr = shepherd.electrumServers[key].abbr; let _wifError = false;
for (let key in shepherd.electrumCoins) {
if (key !== 'auth') {
const _abbr = key;
const _seed = req.body.seed; const _seed = req.body.seed;
let keys; let keys;
let isWif = false;
if (req.body.seed.match('^[a-zA-Z0-9]{34}$') &&
shepherd.appConfig.experimentalFeatures) {
shepherd.log('watchonly pub addr');
shepherd.electrumKeys[_abbr] = {
priv: req.body.seed,
pub: req.body.seed,
};
} else {
try {
bs58check.decode(_seed);
isWif = true;
} catch (e) {}
if ((_seed.length === 51 || _seed.length === 52) && if (isWif) {
_seed.indexOf(' ') === -1 && try {
_seed.match(/^[a-zA-Z0-9]*$/)) { let key = shepherd.isZcash(_abbr.toLowerCase()) ? bitcoinZcash.ECPair.fromWIF(_seed, shepherd.getNetworkData(_abbr.toLowerCase()), true) : bitcoin.ECPair.fromWIF(_seed, shepherd.getNetworkData(_abbr.toLowerCase()), true);
let key = shepherd.bitcoinJS.ECPair.fromWIF(_seed, shepherd.electrumJSNetworks.komodo);
keys = { keys = {
priv: key.toWIF(), priv: key.toWIF(),
pub: key.getAddress(), pub: key.getAddress(),
}; };
} catch (e) {
_wifError = true;
break;
}
} else { } else {
keys = shepherd.seedToWif(_seed, shepherd.findNetworkObj(_abbr), req.body.iguana); keys = shepherd.seedToWif(_seed, shepherd.findNetworkObj(_abbr), req.body.iguana);
} }
@ -22,20 +47,31 @@ module.exports = (shepherd) => {
pub: keys.pub, pub: keys.pub,
}; };
} }
}
}
shepherd.electrumCoins.auth = true; shepherd.electrumCoins.auth = true;
// shepherd.log(JSON.stringify(shepherd.electrumKeys, null, '\t'), true); // shepherd.log(JSON.stringify(shepherd.electrumKeys, null, '\t'), true);
const successObj = { const successObj = {
msg: 'success', msg: _wifError ? 'error' : 'success',
result: 'true', result: 'true',
}; };
res.end(JSON.stringify(successObj)); res.end(JSON.stringify(successObj));
} else {
const errorObj = {
msg: 'error',
result: 'unauthorized access',
};
res.end(JSON.stringify(errorObj));
}
}); });
shepherd.post('/electrum/lock', (req, res, next) => { shepherd.post('/electrum/lock', (req, res, next) => {
if (shepherd.checkToken(req.body.token)) {
shepherd.electrumCoins.auth = false; shepherd.electrumCoins.auth = false;
shepherd.electrumKeys = {}; shepherd.electrumKeys = {};
@ -45,9 +81,18 @@ module.exports = (shepherd) => {
}; };
res.end(JSON.stringify(successObj)); res.end(JSON.stringify(successObj));
} else {
const errorObj = {
msg: 'error',
result: 'unauthorized access',
};
res.end(JSON.stringify(errorObj));
}
}); });
shepherd.post('/electrum/logout', (req, res, next) => { shepherd.post('/electrum/logout', (req, res, next) => {
if (shepherd.checkToken(req.body.token)) {
shepherd.electrumCoins = { shepherd.electrumCoins = {
auth: false, auth: false,
}; };
@ -59,6 +104,14 @@ module.exports = (shepherd) => {
}; };
res.end(JSON.stringify(obj)); res.end(JSON.stringify(obj));
} else {
const errorObj = {
msg: 'error',
result: 'unauthorized access',
};
res.end(JSON.stringify(errorObj));
}
}); });
return shepherd; return shepherd;

21
routes/shepherd/electrum/balance.js

@ -1,5 +1,6 @@
module.exports = (shepherd) => { module.exports = (shepherd) => {
shepherd.get('/electrum/getbalance', (req, res, next) => { shepherd.get('/electrum/getbalance', (req, res, next) => {
if (shepherd.checkToken(req.query.token)) {
const network = req.query.network || shepherd.findNetworkObj(req.query.coin); 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 ecl = new shepherd.electrumJSCore(shepherd.electrumServers[network].port, shepherd.electrumServers[network].address, shepherd.electrumServers[network].proto); // tcp or tls
@ -12,7 +13,6 @@ module.exports = (shepherd) => {
json.hasOwnProperty('confirmed') && json.hasOwnProperty('confirmed') &&
json.hasOwnProperty('unconfirmed')) { json.hasOwnProperty('unconfirmed')) {
if (network === 'komodo') { if (network === 'komodo') {
ecl.connect();
ecl.blockchainAddressListunspent(req.query.address) ecl.blockchainAddressListunspent(req.query.address)
.then((utxoList) => { .then((utxoList) => {
if (utxoList && if (utxoList &&
@ -37,7 +37,7 @@ module.exports = (shepherd) => {
shepherd.Promise.all(_utxo.map((_utxoItem, index) => { shepherd.Promise.all(_utxo.map((_utxoItem, index) => {
return new shepherd.Promise((resolve, reject) => { return new shepherd.Promise((resolve, reject) => {
ecl.blockchainTransactionGet(_utxoItem['tx_hash']) shepherd.getTransaction(_utxoItem['tx_hash'], network, ecl)
.then((_rawtxJSON) => { .then((_rawtxJSON) => {
shepherd.log('electrum gettransaction ==>', true); shepherd.log('electrum gettransaction ==>', true);
shepherd.log((index + ' | ' + (_rawtxJSON.length - 1)), true); shepherd.log((index + ' | ' + (_rawtxJSON.length - 1)), true);
@ -45,7 +45,7 @@ module.exports = (shepherd) => {
// decode tx // decode tx
const _network = shepherd.getNetworkData(network); const _network = shepherd.getNetworkData(network);
const decodedTx = shepherd.electrumJSTxDecoder(_rawtxJSON, _network); const decodedTx = shepherd.electrumJSTxDecoder(_rawtxJSON, network, _network);
if (decodedTx && if (decodedTx &&
decodedTx.format && decodedTx.format &&
@ -80,6 +80,8 @@ module.exports = (shepherd) => {
res.end(JSON.stringify(successObj)); res.end(JSON.stringify(successObj));
}); });
} else { } else {
ecl.close();
const successObj = { const successObj = {
msg: 'success', msg: 'success',
result: { result: {
@ -97,6 +99,8 @@ module.exports = (shepherd) => {
res.end(JSON.stringify(successObj)); res.end(JSON.stringify(successObj));
} }
} else { } else {
ecl.close();
const successObj = { const successObj = {
msg: 'success', msg: 'success',
result: { result: {
@ -132,6 +136,8 @@ module.exports = (shepherd) => {
res.end(JSON.stringify(successObj)); res.end(JSON.stringify(successObj));
} }
} else { } else {
ecl.close();
const successObj = { const successObj = {
msg: 'error', msg: 'error',
result: shepherd.CONNECTION_ERROR_OR_INCOMPLETE_DATA, result: shepherd.CONNECTION_ERROR_OR_INCOMPLETE_DATA,
@ -140,6 +146,15 @@ module.exports = (shepherd) => {
res.end(JSON.stringify(successObj)); res.end(JSON.stringify(successObj));
} }
}); });
} else {
const errorObj = {
msg: 'error',
result: 'unauthorized access',
};
res.end(JSON.stringify(errorObj));
}
}); });
return shepherd; return shepherd;
}; };

18
routes/shepherd/electrum/block.js

@ -1,5 +1,6 @@
module.exports = (shepherd) => { module.exports = (shepherd) => {
shepherd.get('/electrum/getblockinfo', (req, res, next) => { shepherd.get('/electrum/getblockinfo', (req, res, next) => {
if (shepherd.checkToken(req.query.token)) {
shepherd.electrumGetBlockInfo(req.query.height, req.query.network) shepherd.electrumGetBlockInfo(req.query.height, req.query.network)
.then((json) => { .then((json) => {
const successObj = { const successObj = {
@ -9,6 +10,14 @@ module.exports = (shepherd) => {
res.end(JSON.stringify(successObj)); res.end(JSON.stringify(successObj));
}); });
} else {
const errorObj = {
msg: 'error',
result: 'unauthorized access',
};
res.end(JSON.stringify(errorObj));
}
}); });
shepherd.electrumGetBlockInfo = (height, network) => { shepherd.electrumGetBlockInfo = (height, network) => {
@ -28,6 +37,7 @@ module.exports = (shepherd) => {
} }
shepherd.get('/electrum/getcurrentblock', (req, res, next) => { shepherd.get('/electrum/getcurrentblock', (req, res, next) => {
if (shepherd.checkToken(req.query.token)) {
shepherd.electrumGetCurrentBlock(req.query.network) shepherd.electrumGetCurrentBlock(req.query.network)
.then((json) => { .then((json) => {
const successObj = { const successObj = {
@ -37,6 +47,14 @@ module.exports = (shepherd) => {
res.end(JSON.stringify(successObj)); res.end(JSON.stringify(successObj));
}); });
} else {
const errorObj = {
msg: 'error',
result: 'unauthorized access',
};
res.end(JSON.stringify(errorObj));
}
}); });
shepherd.electrumGetCurrentBlock = (network) => { shepherd.electrumGetCurrentBlock = (network) => {

47
routes/shepherd/electrum/coins.js

@ -54,12 +54,42 @@ module.exports = (shepherd) => {
shepherd.log(`${coin} doesnt have any backup electrum servers`, true); shepherd.log(`${coin} doesnt have any backup electrum servers`, true);
} }
if (Object.keys(shepherd.electrumKeys).length > 0) {
const _keys = shepherd.wifToWif(shepherd.electrumKeys[Object.keys(shepherd.electrumKeys)[0]].priv, coin);
shepherd.electrumKeys[coin] = {
priv: _keys.priv,
pub: _keys.pub,
};
}
return true; return true;
} }
} }
} }
shepherd.get('/electrum/coin/changepub', (req, res, next) => {
if (shepherd.checkToken(req.query.token)) {
shepherd.electrumKeys[req.query.coin].pub = req.query.pub;
const successObj = {
msg: 'success',
result: 'true',
};
res.end(JSON.stringify(successObj));
} else {
const errorObj = {
msg: 'error',
result: 'unauthorized access',
};
res.end(JSON.stringify(errorObj));
}
});
shepherd.get('/electrum/coins/add', (req, res, next) => { shepherd.get('/electrum/coins/add', (req, res, next) => {
if (shepherd.checkToken(req.query.token)) {
const result = shepherd.addElectrumCoin(req.query.coin); const result = shepherd.addElectrumCoin(req.query.coin);
const successObj = { const successObj = {
@ -68,9 +98,18 @@ module.exports = (shepherd) => {
}; };
res.end(JSON.stringify(successObj)); res.end(JSON.stringify(successObj));
} else {
const errorObj = {
msg: 'error',
result: 'unauthorized access',
};
res.end(JSON.stringify(errorObj));
}
}); });
shepherd.get('/electrum/coins', (req, res, next) => { shepherd.get('/electrum/coins', (req, res, next) => {
if (shepherd.checkToken(req.query.token)) {
let _electrumCoins = JSON.parse(JSON.stringify(shepherd.electrumCoins)); // deep cloning let _electrumCoins = JSON.parse(JSON.stringify(shepherd.electrumCoins)); // deep cloning
for (let key in _electrumCoins) { for (let key in _electrumCoins) {
@ -85,6 +124,14 @@ module.exports = (shepherd) => {
}; };
res.end(JSON.stringify(successObj)); res.end(JSON.stringify(successObj));
} else {
const errorObj = {
msg: 'error',
result: 'unauthorized access',
};
res.end(JSON.stringify(errorObj));
}
}); });
return shepherd; return shepherd;

500
routes/shepherd/electrum/createtx-multi.js

@ -0,0 +1,500 @@
const bitcoinJSForks = require('bitcoinforksjs-lib');
const bitcoinZcash = require('bitcoinjs-lib-zcash');
const bitcoinPos = require('bitcoinjs-lib-pos');
// not prod ready, only for voting!
// needs a fix
module.exports = (shepherd) => {
shepherd.post('/electrum/createrawtx-multiout', (req, res, next) => {
if (shepherd.checkToken(req.body.token)) {
// TODO: 1) unconf output(s) error message
// 2) check targets integrity
const network = req.body.network || shepherd.findNetworkObj(req.body.coin);
const ecl = new shepherd.electrumJSCore(shepherd.electrumServers[network].port, shepherd.electrumServers[network].address, shepherd.electrumServers[network].proto); // tcp or tls
const initTargets = JSON.parse(JSON.stringify(req.body.targets));
let targets = req.body.targets;
const changeAddress = req.body.change;
const push = req.body.push;
const opreturn = req.body.opreturn;
const btcFee = req.body.btcfee ? Number(req.body.btcfee) : null;
let fee = shepherd.electrumServers[network].txfee;
let wif = req.body.wif;
if (req.body.gui) {
wif = shepherd.electrumKeys[req.body.coin].priv;
}
if (req.body.vote) {
wif = shepherd.elections.priv;
}
if (btcFee) {
fee = 0;
}
shepherd.log('electrum createrawtx =>', true);
ecl.connect();
shepherd.listunspent(ecl, changeAddress, network, true, req.body.verify === 'true' ? true : null)
.then((utxoList) => {
ecl.close();
if (utxoList &&
utxoList.length &&
utxoList[0] &&
utxoList[0].txid) {
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);
targets[0].value = targets[0].value + fee;
shepherd.log(`default fee ${fee}`, true);
shepherd.log(`targets ==>`, true);
shepherd.log(targets, true);
// 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
const firstRun = shepherd.coinSelect(utxoListFormatted, targets, btcFee ? btcFee : 0);
let inputs = firstRun.inputs;
let outputs = firstRun.outputs;
if (btcFee) {
shepherd.log(`btc fee per byte ${btcFee}`, true);
fee = firstRun.fee;
}
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 (!outputs) {
targets[0].value = targets[0].value - fee;
shepherd.log('second run', true);
shepherd.log('coinselect adjusted targets =>', true);
shepherd.log(targets, true);
const secondRun = shepherd.coinSelect(utxoListFormatted, targets, 0);
inputs = secondRun.inputs;
outputs = secondRun.outputs;
fee = fee ? fee : secondRun.fee;
shepherd.log('second run coinselect inputs =>', true);
shepherd.log(inputs, true);
shepherd.log('second run coinselect outputs =>', true);
shepherd.log(outputs, true);
shepherd.log('second run coinselect fee =>', true);
shepherd.log(fee, true);
}
let _change = 0;
if (outputs &&
outputs.length > 1) {
_change = outputs[outputs.length - 1].value - fee;
}
if (!btcFee &&
_change === 0) {
outputs[0].value = outputs[0].value - fee;
}
shepherd.log('adjusted outputs');
shepherd.log(outputs, true);
shepherd.log('init targets', true);
shepherd.log(initTargets, true);
if (initTargets[0].value < targets[0].value) {
targets[0].value = initTargets[0].value;
}
let _targetsSum = 0;
for (let i = 0; i < targets.length; i++) {
_targetsSum += Number(targets[i].value);
}
shepherd.log(`total targets sum ${_targetsSum}`);
/*if (btcFee) {
value = _targetsSum;
} else {
if (_change > 0) {
value = _targetsSum - fee;
}
}*/
value = _targetsSum;
shepherd.log('adjusted outputs, value - default fee =>', true);
shepherd.log(outputs, true);
// 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;
const _feeOverhead = 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);
if (_maxSpend - fee === value) {
_change = totalInterest - _change - _feeOverhead;
if (outputAddress === changeAddress) {
value += _change;
_change = 0;
shepherd.log(`send to self ${outputAddress} = ${changeAddress}`, true);
shepherd.log(`send to self old val ${value}, new val ${value + _change}`, true);
}
} else {
_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;
}
let voutSum = 0;
for (let i = 0; i < outputs.length; i++) {
voutSum += outputs[i].value;
}
const _estimatedFee = vinSum - voutSum;
shepherd.log(`vin sum ${vinSum} (${vinSum * 0.00000001})`, true);
shepherd.log(`vout sum ${voutSum} (${voutSum * 0.00000001})`, true);
shepherd.log(`estimatedFee ${_estimatedFee} (${_estimatedFee * 0.00000001})`, true);
// double check no extra fee is applied
shepherd.log(`vin - vout - change ${vinSum - value - _change}`);
if ((vinSum - value - _change) > fee) {
_change += fee;
shepherd.log(`double fee, increase change by ${fee}`);
shepherd.log(`adjusted vin - vout - change ${vinSum - value - _change}`);
}
// TODO: use individual dust thresholds
if (_change > 0 &&
_change <= 1000) {
shepherd.log(`change is < 1000 sats, donate ${_change} sats to miners`, true);
_change = 0;
}
outputAddress = outputs;
if (outputAddress.length > 1) {
outputAddress.pop();
}
let _rawtx;
if (network === 'btg' ||
network === 'bch') {
/*_rawtx = shepherd.buildSignedTxForks(
outputAddress,
changeAddress,
wif,
network,
inputs,
_change,
value
);*/
} else {
_rawtx = shepherd.buildSignedTxMulti(
outputAddress,
changeAddress,
wif,
network,
inputs,
_change,
value,
opreturn
);
}
if (!push ||
push === 'false') {
const successObj = {
msg: 'success',
result: {
utxoSet: inputs,
change: _change,
changeAdjusted: _change,
totalInterest,
// 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();
const _rawObj = {
utxoSet: inputs,
change: _change,
changeAdjusted: _change,
totalInterest,
fee,
value,
outputAddress,
changeAddress,
network,
rawtx: _rawtx,
txid,
utxoVerified,
};
if (txid &&
txid.indexOf('bad-txns-inputs-spent') > -1) {
const successObj = {
msg: 'error',
result: 'Bad transaction inputs spent',
raw: _rawObj,
};
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: _rawObj,
};
res.end(JSON.stringify(successObj));
} else {
const successObj = {
msg: 'success',
result: _rawObj,
};
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: _rawObj,
};
res.end(JSON.stringify(successObj));
} else {
const successObj = {
msg: 'error',
result: 'Can\'t broadcast transaction',
raw: _rawObj,
};
res.end(JSON.stringify(successObj));
}
}
}
});
}
}
}
} else {
const successObj = {
msg: 'error',
result: utxoList,
};
res.end(JSON.stringify(successObj));
}
});
} else {
const errorObj = {
msg: 'error',
result: 'unauthorized access',
};
res.end(JSON.stringify(errorObj));
}
});
// single sig
shepherd.buildSignedTxMulti = (sendTo, changeAddress, wif, network, utxo, changeValue, spendValue, opreturn) => {
let key = shepherd.isZcash(network) ? bitcoinZcash.ECPair.fromWIF(wif, shepherd.getNetworkData(network)) : shepherd.bitcoinJS.ECPair.fromWIF(wif, shepherd.getNetworkData(network));
let tx;
if (shepherd.isZcash(network)) {
tx = new bitcoinZcash.TransactionBuilder(shepherd.getNetworkData(network));
} else if (shepherd.isPos(network)) {
tx = new bitcoinPos.TransactionBuilder(shepherd.getNetworkData(network));
} else {
tx = new shepherd.bitcoinJS.TransactionBuilder(shepherd.getNetworkData(network));
}
shepherd.log('buildSignedTx', true);
// 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);
}
for (let i = 0; i < sendTo.length; i++) {
if (shepherd.isPos(network)) {
tx.addOutput(sendTo[i].address, Number(sendTo[i].value), shepherd.getNetworkData(network));
} else {
tx.addOutput(sendTo[i].address, Number(sendTo[i].value));
}
}
if (changeValue > 0) {
if (shepherd.isPos(network)) {
tx.addOutput(changeAddress, Number(changeValue), shepherd.getNetworkData(network));
} else {
tx.addOutput(changeAddress, Number(changeValue));
}
}
if (opreturn &&
opreturn.length) {
for (let i = 0; i < opreturn.length; i++) {
shepherd.log(`opreturn ${i} ${opreturn[i]}`);
const data = Buffer.from(opreturn[i], 'utf8');
const dataScript = shepherd.bitcoinJS.script.nullData.output.encode(data);
tx.addOutput(dataScript, 1000);
}
}
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++) {
if (shepherd.isPos(network)) {
tx.sign(shepherd.getNetworkData(network), i, key);
} else {
tx.sign(i, key);
}
}
const rawtx = tx.build().toHex();
shepherd.log('buildSignedTx signed tx hex', true);
shepherd.log(rawtx, true);
return rawtx;
}
return shepherd;
};

86
routes/shepherd/electrum/createtx-split.js

@ -0,0 +1,86 @@
const bitcoinJSForks = require('bitcoinforksjs-lib');
const bitcoinZcash = require('bitcoinjs-lib-zcash');
const bitcoinPos = require('bitcoinjs-lib-pos');
module.exports = (shepherd) => {
// utxo split 1 -> 1, multiple outputs
shepherd.post('/electrum/createrawtx-split', (req, res, next) => {
if (shepherd.checkToken(req.body.token)) {
const wif = req.body.payload.wif;
const utxo = req.body.payload.utxo;
const targets = req.body.payload.targets;
const network = req.body.payload.network;
const change = req.body.payload.change;
const outputAddress = req.body.payload.outputAddress;
const changeAddress = req.body.payload.changeAddress;
let key = shepherd.isZcash(network) ? bitcoinZcash.ECPair.fromWIF(wif, shepherd.getNetworkData(network)) : shepherd.bitcoinJS.ECPair.fromWIF(wif, shepherd.getNetworkData(network));
let tx;
if (shepherd.isZcash(network)) {
tx = new bitcoinZcash.TransactionBuilder(shepherd.getNetworkData(network));
} else if (shepherd.isPos(network)) {
tx = new bitcoinPos.TransactionBuilder(shepherd.getNetworkData(network));
} else {
tx = new shepherd.bitcoinJS.TransactionBuilder(shepherd.getNetworkData(network));
}
shepherd.log('buildSignedTx', true);
shepherd.log(`buildSignedTx pub key ${key.getAddress().toString()}`, true);
for (let i = 0; i < utxo.length; i++) {
tx.addInput(utxo[i].txid, utxo[i].vout);
}
for (let i = 0; i < targets.length; i++) {
if (shepherd.isPos(network)) {
tx.addOutput(outputAddress, Number(targets[i]), shepherd.getNetworkData(network));
} else {
tx.addOutput(outputAddress, Number(targets[i]));
}
}
if (Number(change) > 0) {
if (shepherd.isPos(network)) {
tx.addOutput(changeAddress, Number(change), shepherd.getNetworkData(network));
} else {
shepherd.log(`change ${change}`, true);
tx.addOutput(changeAddress, Number(change));
}
}
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);
}
for (let i = 0; i < utxo.length; i++) {
if (shepherd.isPos(network)) {
tx.sign(shepherd.getNetworkData(network), i, key);
} else {
tx.sign(i, key);
}
}
const rawtx = tx.build().toHex();
const successObj = {
msg: 'success',
result: rawtx,
};
res.end(JSON.stringify(successObj));
} else {
const errorObj = {
msg: 'error',
result: 'unauthorized access',
};
res.end(JSON.stringify(errorObj));
}
});
return shepherd;
};

304
routes/shepherd/electrum/createtx.js

@ -1,9 +1,22 @@
const bitcoinJSForks = require('bitcoinforksjs-lib');
const bitcoinZcash = require('bitcoinjs-lib-zcash');
const bitcoinPos = require('bitcoinjs-lib-pos');
module.exports = (shepherd) => { module.exports = (shepherd) => {
// unsigned tx // unsigned tx
shepherd.buildUnsignedTx = (sendTo, changeAddress, network, utxo, changeValue, spendValue) => { shepherd.buildUnsignedTx = (sendTo, changeAddress, network, utxo, changeValue, spendValue) => {
let tx = new shepherd.bitcoinJS.TransactionBuilder(shepherd.getNetworkData(network)); let tx;
// TODO: finish unsigned for zcash, btc forks and pos coins
if (network === 'btg') {
shepherd.log('enable btg', true);
tx = new bitcoinJSForks.TransactionBuilder(shepherd.getNetworkData(network));
tx.enableBitcoinGold(true);
} else {
tx = new shepherd.bitcoinJS.TransactionBuilder(shepherd.getNetworkData(network));
}
shepherd.log('buildSignedTx'); shepherd.log('buildSignedTx', true);
// console.log(`buildSignedTx priv key ${wif}`); // console.log(`buildSignedTx priv key ${wif}`);
shepherd.log(`buildSignedTx pub key ${changeAddress}`, true); shepherd.log(`buildSignedTx pub key ${changeAddress}`, true);
// console.log('buildSignedTx std tx fee ' + shepherd.electrumServers[network].txfee); // console.log('buildSignedTx std tx fee ' + shepherd.electrumServers[network].txfee);
@ -41,11 +54,19 @@ module.exports = (shepherd) => {
} }
// single sig // single sig
shepherd.buildSignedTx = (sendTo, changeAddress, wif, network, utxo, changeValue, spendValue) => { shepherd.buildSignedTx = (sendTo, changeAddress, wif, network, utxo, changeValue, spendValue, opreturn) => {
let key = shepherd.bitcoinJS.ECPair.fromWIF(wif, shepherd.getNetworkData(network)); let key = shepherd.isZcash(network) ? bitcoinZcash.ECPair.fromWIF(wif, shepherd.getNetworkData(network)) : shepherd.bitcoinJS.ECPair.fromWIF(wif, shepherd.getNetworkData(network));
let tx = new shepherd.bitcoinJS.TransactionBuilder(shepherd.getNetworkData(network)); let tx;
if (shepherd.isZcash(network)) {
tx = new bitcoinZcash.TransactionBuilder(shepherd.getNetworkData(network));
} else if (shepherd.isPos(network)) {
tx = new bitcoinPos.TransactionBuilder(shepherd.getNetworkData(network));
} else {
tx = new shepherd.bitcoinJS.TransactionBuilder(shepherd.getNetworkData(network));
}
shepherd.log('buildSignedTx'); shepherd.log('buildSignedTx', true);
// console.log(`buildSignedTx priv key ${wif}`); // console.log(`buildSignedTx priv key ${wif}`);
shepherd.log(`buildSignedTx pub key ${key.getAddress().toString()}`, true); shepherd.log(`buildSignedTx pub key ${key.getAddress().toString()}`, true);
// console.log('buildSignedTx std tx fee ' + shepherd.electrumServers[network].txfee); // console.log('buildSignedTx std tx fee ' + shepherd.electrumServers[network].txfee);
@ -54,11 +75,26 @@ module.exports = (shepherd) => {
tx.addInput(utxo[i].txid, utxo[i].vout); tx.addInput(utxo[i].txid, utxo[i].vout);
} }
if (shepherd.isPos(network)) {
tx.addOutput(sendTo, Number(spendValue), shepherd.getNetworkData(network));
} else {
tx.addOutput(sendTo, Number(spendValue)); tx.addOutput(sendTo, Number(spendValue));
}
if (changeValue > 0) { if (changeValue > 0) {
if (shepherd.isPos(network)) {
tx.addOutput(changeAddress, Number(changeValue), shepherd.getNetworkData(network));
} else {
tx.addOutput(changeAddress, Number(changeValue)); tx.addOutput(changeAddress, Number(changeValue));
} }
}
if (opreturn) {
console.log(`opreturn ${opreturn}`);
const data = Buffer.from(opreturn, 'utf8');
const dataScript = shepherd.bitcoinJS.script.nullData.output.encode(data);
tx.addOutput(dataScript, 1000);
}
if (network === 'komodo' || if (network === 'komodo' ||
network === 'KMD') { network === 'KMD') {
@ -75,8 +111,66 @@ module.exports = (shepherd) => {
shepherd.log(tx, true); shepherd.log(tx, true);
for (let i = 0; i < utxo.length; i++) { for (let i = 0; i < utxo.length; i++) {
if (shepherd.isPos(network)) {
tx.sign(shepherd.getNetworkData(network), i, key);
} else {
tx.sign(i, key); tx.sign(i, key);
} }
}
const rawtx = tx.build().toHex();
shepherd.log('buildSignedTx signed tx hex', true);
shepherd.log(rawtx, true);
return rawtx;
}
// btg
shepherd.buildSignedTxForks = (sendTo, changeAddress, wif, network, utxo, changeValue, spendValue) => {
let tx;
if (network === 'btg' ||
network === 'bch') {
tx = new bitcoinJSForks.TransactionBuilder(shepherd.getNetworkData(network));
}
const keyPair = bitcoinJSForks.ECPair.fromWIF(wif, shepherd.getNetworkData(network));
const pk = bitcoinJSForks.crypto.hash160(keyPair.getPublicKeyBuffer());
const spk = bitcoinJSForks.script.pubKeyHash.output.encode(pk);
shepherd.log(`buildSignedTx${network.toUpperCase()}`, true);
for (let i = 0; i < utxo.length; i++) {
tx.addInput(utxo[i].txid, utxo[i].vout, bitcoinJSForks.Transaction.DEFAULT_SEQUENCE, spk);
}
tx.addOutput(sendTo, Number(spendValue));
if (changeValue > 0) {
tx.addOutput(changeAddress, Number(changeValue));
}
if (network === 'btg') {
tx.enableBitcoinGold(true);
} else if (network === 'bch') {
tx.enableBitcoinCash(true);
}
tx.setVersion(2);
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);
const hashType = bitcoinJSForks.Transaction.SIGHASH_ALL | bitcoinJSForks.Transaction.SIGHASH_BITCOINCASHBIP143;
for (let i = 0; i < utxo.length; i++) {
tx.sign(i, keyPair, null, hashType, utxo[i].value);
}
const rawtx = tx.build().toHex(); const rawtx = tx.build().toHex();
@ -101,29 +195,42 @@ module.exports = (shepherd) => {
} }
shepherd.get('/electrum/createrawtx', (req, res, next) => { shepherd.get('/electrum/createrawtx', (req, res, next) => {
// txid 64 char if (shepherd.checkToken(req.query.token)) {
// TODO: unconf output(s) error message
const network = req.query.network || shepherd.findNetworkObj(req.query.coin); 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 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 outputAddress = req.query.address;
const changeAddress = req.query.change; const changeAddress = req.query.change;
let value = Number(req.query.value);
const push = req.query.push; const push = req.query.push;
const fee = shepherd.electrumServers[network].txfee; const opreturn = req.query.opreturn;
const btcFee = req.query.btcfee ? Number(req.query.btcfee) : null;
let fee = shepherd.electrumServers[network].txfee;
let value = Number(req.query.value);
let wif = req.query.wif; let wif = req.query.wif;
if (req.query.gui) { if (req.query.gui) {
wif = shepherd.electrumKeys[req.query.coin].priv; wif = shepherd.electrumKeys[req.query.coin].priv;
} }
if (req.query.vote) {
wif = shepherd.elections.priv;
}
if (btcFee) {
fee = 0;
}
shepherd.log('electrum createrawtx =>', true); shepherd.log('electrum createrawtx =>', true);
ecl.connect(); ecl.connect();
shepherd.listunspent(ecl, changeAddress, network, true, true) shepherd.listunspent(ecl, changeAddress, network, true, req.query.verify === 'true' ? true : null)
.then((utxoList) => { .then((utxoList) => {
ecl.close(); ecl.close();
if (utxoList && if (utxoList &&
utxoList.length) { utxoList.length &&
utxoList[0] &&
utxoList[0].txid) {
let utxoListFormatted = []; let utxoListFormatted = [];
let totalInterest = 0; let totalInterest = 0;
let totalInterestUTXOCount = 0; let totalInterestUTXOCount = 0;
@ -163,12 +270,23 @@ module.exports = (shepherd) => {
shepherd.log('targets =>', true); shepherd.log('targets =>', true);
shepherd.log(targets, true); shepherd.log(targets, true);
const feeRate = 20; // sats/byte targets[0].value = targets[0].value + fee;
shepherd.log(`default fee ${fee}`, true);
shepherd.log(`targets ==>`, true);
shepherd.log(targets, true);
// default coin selection algo blackjack with fallback to accumulative // default coin selection algo blackjack with fallback to accumulative
// make a first run, calc approx tx fee // make a first run, calc approx tx fee
// if ins and outs are empty reduce max spend by txfee // if ins and outs are empty reduce max spend by txfee
let { inputs, outputs, fee } = shepherd.coinSelect(utxoListFormatted, targets, feeRate); const firstRun = shepherd.coinSelect(utxoListFormatted, targets, btcFee ? btcFee : 0);
let inputs = firstRun.inputs;
let outputs = firstRun.outputs;
if (btcFee) {
shepherd.log(`btc fee per byte ${btcFee}`, true);
fee = firstRun.fee;
}
shepherd.log('coinselect res =>', true); shepherd.log('coinselect res =>', true);
shepherd.log('coinselect inputs =>', true); shepherd.log('coinselect inputs =>', true);
@ -184,10 +302,10 @@ module.exports = (shepherd) => {
shepherd.log('coinselect adjusted targets =>', true); shepherd.log('coinselect adjusted targets =>', true);
shepherd.log(targets, true); shepherd.log(targets, true);
const secondRun = shepherd.coinSelect(utxoListFormatted, targets, feeRate); const secondRun = shepherd.coinSelect(utxoListFormatted, targets, 0);
inputs = secondRun.inputs; inputs = secondRun.inputs;
outputs = secondRun.outputs; outputs = secondRun.outputs;
fee = secondRun.fee; fee = fee ? fee : secondRun.fee;
shepherd.log('second run coinselect inputs =>', true); shepherd.log('second run coinselect inputs =>', true);
shepherd.log(inputs, true); shepherd.log(inputs, true);
@ -201,9 +319,25 @@ module.exports = (shepherd) => {
if (outputs && if (outputs &&
outputs.length === 2) { outputs.length === 2) {
_change = outputs[1].value; _change = outputs[1].value - fee;
}
if (!btcFee &&
_change === 0) {
outputs[0].value = outputs[0].value - fee;
} }
if (btcFee) {
value = outputs[0].value;
} else {
if (_change > 0) {
value = outputs[0].value - fee;
}
}
shepherd.log('adjusted outputs, value - default fee =>', true);
shepherd.log(outputs, true);
// check if any outputs are unverified // check if any outputs are unverified
if (inputs && if (inputs &&
inputs.length) { inputs.length) {
@ -241,13 +375,14 @@ module.exports = (shepherd) => {
if (network === 'komodo' && if (network === 'komodo' &&
totalInterest > 0) { totalInterest > 0) {
// account for extra vout // account for extra vout
const _feeOverhead = outputs.length === 1 ? shepherd.estimateTxSize(0, 1) * feeRate : 0; // const _feeOverhead = outputs.length === 1 ? shepherd.estimateTxSize(0, 1) * feeRate : 0;
const _feeOverhead = 0;
shepherd.log(`max interest to claim ${totalInterest} (${totalInterest * 0.00000001})`, true); shepherd.log(`max interest to claim ${totalInterest} (${totalInterest * 0.00000001})`, true);
shepherd.log(`estimated fee overhead ${_feeOverhead}`, 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); shepherd.log(`current change amount ${_change} (${_change * 0.00000001}), boosted change amount ${_change + (totalInterest - _feeOverhead)} (${(_change + (totalInterest - _feeOverhead)) * 0.00000001})`, true);
if (_maxSpend === value) { if (_maxSpend - fee === value) {
_change = totalInterest - _change - _feeOverhead; _change = totalInterest - _change - _feeOverhead;
if (outputAddress === changeAddress) { if (outputAddress === changeAddress) {
@ -280,6 +415,20 @@ module.exports = (shepherd) => {
shepherd.log(`vin sum ${vinSum} (${vinSum * 0.00000001})`, true); shepherd.log(`vin sum ${vinSum} (${vinSum * 0.00000001})`, true);
shepherd.log(`estimatedFee ${_estimatedFee} (${_estimatedFee * 0.00000001})`, true); shepherd.log(`estimatedFee ${_estimatedFee} (${_estimatedFee * 0.00000001})`, true);
// double check no extra fee is applied
shepherd.log(`vin - vout ${vinSum - value - _change}`);
if ((vinSum - value - _change) > fee) {
_change += fee;
shepherd.log(`double fee, increase change by ${fee}`);
}
// TODO: use individual dust thresholds
if (_change > 0 &&
_change <= 1000) {
shepherd.log(`change is < 1000 sats, donate ${_change} sats to miners`, true);
_change = 0;
}
let _rawtx; let _rawtx;
@ -293,7 +442,10 @@ module.exports = (shepherd) => {
value value
); );
} else { } else {
_rawtx = shepherd.buildSignedTx( if (!req.query.offline) {
if (network === 'btg' ||
network === 'bch') {
_rawtx = shepherd.buildSignedTxForks(
outputAddress, outputAddress,
changeAddress, changeAddress,
wif, wif,
@ -302,6 +454,19 @@ module.exports = (shepherd) => {
_change, _change,
value value
); );
} else {
_rawtx = shepherd.buildSignedTx(
outputAddress,
changeAddress,
wif,
network,
inputs,
_change,
value,
opreturn
);
}
}
} }
if (!push || if (!push ||
@ -333,12 +498,7 @@ module.exports = (shepherd) => {
.then((txid) => { .then((txid) => {
ecl.close(); ecl.close();
if (txid && const _rawObj = {
txid.indexOf('bad-txns-inputs-spent') > -1) {
const successObj = {
msg: 'error',
result: 'Bad transaction inputs spent',
raw: {
utxoSet: inputs, utxoSet: inputs,
change: _change, change: _change,
changeAdjusted: _change, changeAdjusted: _change,
@ -351,7 +511,14 @@ module.exports = (shepherd) => {
rawtx: _rawtx, rawtx: _rawtx,
txid, txid,
utxoVerified, utxoVerified,
}, };
if (txid &&
txid.indexOf('bad-txns-inputs-spent') > -1) {
const successObj = {
msg: 'error',
result: 'Bad transaction inputs spent',
raw: _rawObj,
}; };
res.end(JSON.stringify(successObj)); res.end(JSON.stringify(successObj));
@ -362,41 +529,14 @@ module.exports = (shepherd) => {
const successObj = { const successObj = {
msg: 'error', msg: 'error',
result: 'Bad transaction inputs spent', result: 'Bad transaction inputs spent',
raw: { raw: _rawObj,
utxoSet: inputs,
change: _change,
changeAdjusted: _change,
totalInterest,
fee,
value,
outputAddress,
changeAddress,
network,
rawtx: _rawtx,
txid,
utxoVerified,
},
}; };
res.end(JSON.stringify(successObj)); res.end(JSON.stringify(successObj));
} else { } else {
const successObj = { const successObj = {
msg: 'success', msg: 'success',
result: { result: _rawObj,
utxoSet: inputs,
change: _change,
changeAdjusted: _change,
totalInterest,
fee,
// wif,
value,
outputAddress,
changeAddress,
network,
rawtx: _rawtx,
txid,
utxoVerified,
},
}; };
res.end(JSON.stringify(successObj)); res.end(JSON.stringify(successObj));
@ -407,20 +547,7 @@ module.exports = (shepherd) => {
const successObj = { const successObj = {
msg: 'error', msg: 'error',
result: 'Bad transaction inputs spent', result: 'Bad transaction inputs spent',
raw: { raw: _rawObj,
utxoSet: inputs,
change: _change,
changeAdjusted: _change,
totalInterest,
fee,
value,
outputAddress,
changeAddress,
network,
rawtx: _rawtx,
txid,
utxoVerified,
},
}; };
res.end(JSON.stringify(successObj)); res.end(JSON.stringify(successObj));
@ -428,20 +555,7 @@ module.exports = (shepherd) => {
const successObj = { const successObj = {
msg: 'error', msg: 'error',
result: 'Can\'t broadcast transaction', result: 'Can\'t broadcast transaction',
raw: { raw: _rawObj,
utxoSet: inputs,
change: _change,
changeAdjusted: _change,
totalInterest,
fee,
value,
outputAddress,
changeAddress,
network,
rawtx: _rawtx,
txid,
utxoVerified,
},
}; };
res.end(JSON.stringify(successObj)); res.end(JSON.stringify(successObj));
@ -461,11 +575,21 @@ module.exports = (shepherd) => {
res.end(JSON.stringify(successObj)); res.end(JSON.stringify(successObj));
} }
}); });
} else {
const errorObj = {
msg: 'error',
result: 'unauthorized access',
};
res.end(JSON.stringify(errorObj));
}
}); });
shepherd.get('/electrum/pushtx', (req, res, next) => { shepherd.post('/electrum/pushtx', (req, res, next) => {
const rawtx = req.query.rawtx; if (shepherd.checkToken(req.body.token)) {
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 const rawtx = req.body.rawtx;
const _network = req.body.network;
const ecl = new shepherd.electrumJSCore(shepherd.electrumServers[_network].port, shepherd.electrumServers[_network].address, shepherd.electrumServers[_network].proto); // tcp or tls
ecl.connect(); ecl.connect();
ecl.blockchainTransactionBroadcast(rawtx) ecl.blockchainTransactionBroadcast(rawtx)
@ -481,6 +605,14 @@ module.exports = (shepherd) => {
res.end(JSON.stringify(successObj)); res.end(JSON.stringify(successObj));
}); });
} else {
const errorObj = {
msg: 'error',
result: 'unauthorized access',
};
res.end(JSON.stringify(errorObj));
}
}); });
return shepherd; return shepherd;

9
routes/shepherd/electrum/estimate.js

@ -1,5 +1,6 @@
module.exports = (shepherd) => { module.exports = (shepherd) => {
shepherd.get('/electrum/estimatefee', (req, res, next) => { shepherd.get('/electrum/estimatefee', (req, res, next) => {
if (shepherd.checkToken(req.query.token)) {
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 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.connect();
@ -15,6 +16,14 @@ module.exports = (shepherd) => {
res.end(JSON.stringify(successObj)); res.end(JSON.stringify(successObj));
}); });
} else {
const errorObj = {
msg: 'error',
result: 'unauthorized access',
};
res.end(JSON.stringify(errorObj));
}
}); });
shepherd.estimateTxSize = (numVins, numOuts) => { shepherd.estimateTxSize = (numVins, numOuts) => {

2
routes/shepherd/electrum/interest.js

@ -19,7 +19,7 @@ module.exports = (shepherd) => {
} }
timestampDiffMinutes -= 59; timestampDiffMinutes -= 59;
shepherd.log(`minutes if statement ${timestampDiffMinutes}`, true); // shepherd.log(`minutes if statement ${timestampDiffMinutes}`, true);
// TODO: check if interest is > 5% yr // TODO: check if interest is > 5% yr
// calc ytd and 5% for 1 yr // calc ytd and 5% for 1 yr

267
routes/shepherd/electrum/keys.js

@ -1,11 +1,28 @@
const sha256 = require('js-sha256'); const sha256 = require('js-sha256');
const buggySha256 = require('sha256');
const bip39 = require('bip39');
const crypto = require('crypto');
const bigi = require('bigi');
const bitcoinZcash = require('bitcoinjs-lib-zcash');
const bitcoin = require('bitcoinjs-lib');
const bs58check = require('bs58check');
module.exports = (shepherd) => { module.exports = (shepherd) => {
shepherd.wifToWif = (wif, network) => {
network = network === 'KMD' ? 'komodo' : network.toLowerCase();
const key = shepherd.isZcash(network) ? new bitcoinZcash.ECPair.fromWIF(wif, shepherd.getNetworkData(network), true) : new bitcoin.ECPair.fromWIF(wif, shepherd.getNetworkData(network), true);
return {
pub: key.getAddress(),
priv: key.toWIF(),
};
}
shepherd.seedToWif = (seed, network, iguana) => { shepherd.seedToWif = (seed, network, iguana) => {
let bytes; let bytes;
if (process.argv.indexOf('spvold=true') > -1) { if (process.argv.indexOf('spvold=true') > -1) {
bytes = shepherd.sha256(seed, { asBytes: true }); bytes = buggySha256(seed, { asBytes: true });
} else { } else {
const hash = sha256.create().update(seed); const hash = sha256.create().update(seed);
bytes = hash.array(); bytes = hash.array();
@ -17,34 +34,72 @@ module.exports = (shepherd) => {
bytes[31] |= 64; bytes[31] |= 64;
} }
const toHexString = (byteArray) => { const d = bigi.fromBuffer(bytes);
return Array.from(byteArray, (byte) => { const keyPair = shepherd.isZcash(network) ? new bitcoinZcash.ECPair(d, null, { network: shepherd.getNetworkData(network) }) : new bitcoin.ECPair(d, null, { network: shepherd.getNetworkData(network) });
return ('0' + (byte & 0xFF).toString(16)).slice(-2); const keys = {
}).join(''); pub: keyPair.getAddress(),
priv: keyPair.toWIF(),
};
/*shepherd.log(`seed: ${seed}`, true);
shepherd.log(`network ${network}`, true);
shepherd.log(`seedtowif priv key ${keys.priv}`, true);
shepherd.log(`seedtowif pub key ${keys.pub}`, true);*/
return keys;
} }
const hex = toHexString(bytes); shepherd.get('/electrum/wiftopub', (req, res, next) => {
if (shepherd.checkToken(req.query.token)) {
let key = shepherd.isZcash(req.query.coin.toLowerCase()) ? bitcoinZcash.ECPair.fromWIF(req.query.wif, shepherd.electrumJSNetworks[req.query.coin], true) : bitcoin.ECPair.fromWIF(req.query.wif, shepherd.electrumJSNetworks[req.query.coin], true);
keys = {
priv: key.toWIF(),
pub: key.getAddress(),
};
const key = new shepherd.CoinKey(new Buffer(hex, 'hex'), { const successObj = {
private: shepherd.getNetworkData(network).wif, msg: 'success',
public: shepherd.getNetworkData(network).pubKeyHash, result: {
keys,
},
};
res.end(JSON.stringify(successObj));
} else {
const errorObj = {
msg: 'error',
result: 'unauthorized access',
};
res.end(JSON.stringify(errorObj));
}
}); });
key.compressed = true; shepherd.post('/electrum/seedtowif', (req, res, next) => {
if (shepherd.checkToken(req.body.token)) {
let keys = shepherd.seedToWif(req.body.seed, req.body.network.toLowerCase(), req.body.iguana);
// shepherd.log(`seed: ${seed}`, true); const successObj = {
// shepherd.log(`network ${network}`, true); msg: 'success',
// shepherd.log(`seedtowif priv key ${key.privateWif}`, true); result: {
// shepherd.log(`seedtowif pub key ${key.publicAddress}`, true); keys,
},
};
return { res.end(JSON.stringify(successObj));
priv: key.privateWif, } else {
pub: key.publicAddress, const errorObj = {
msg: 'error',
result: 'unauthorized access',
}; };
res.end(JSON.stringify(errorObj));
} }
});
shepherd.get('/electrum/seedtowif', (req, res, next) => { shepherd.post('/electrum/seedtowif', (req, res, next) => {
const keys = shepherd.seedToWif(req.query.seed, req.query.network, req.query.iguana); if (shepherd.checkToken(req.body.token)) {
let keys = shepherd.seedToWif(req.body.seed, req.body.network.toLowerCase(), req.body.iguana);
const successObj = { const successObj = {
msg: 'success', msg: 'success',
@ -54,20 +109,101 @@ module.exports = (shepherd) => {
}; };
res.end(JSON.stringify(successObj)); res.end(JSON.stringify(successObj));
} else {
const errorObj = {
msg: 'error',
result: 'unauthorized access',
};
res.end(JSON.stringify(errorObj));
}
}); });
shepherd.getCoinByPub = (address) => {
const _skipNetworks = ['btc', 'crw', 'dgb', 'arg', 'zec', 'nmc', 'ltc', 'vtc', 'via', 'fair', 'doge', 'kmd', 'mona'];
try {
const _b58check = shepherd.isZcash(network.toLowerCase()) ? bitcoinZcash.address.fromBase58Check(address) : bitcoin.address.fromBase58Check(address);
let _coin = [];
let returnObj;
for (let key in shepherd.electrumJSNetworks) {
if (_b58check.version === shepherd.electrumJSNetworks[key].pubKeyHash &&
!_skipNetworks.find((item) => { return item === key ? true : false })) {
_coin.push(key);
}
}
if (_coin.length) {
return {
coin: _coin,
version: _b58check.version,
};
} else {
return 'Unable to find matching coin version';
}
} catch(e) {
return 'Invalid pub address';
}
};
shepherd.addressVersionCheck = (network, address) => {
const _network = shepherd.getNetworkData(network.toLowerCase());
try {
const _b58check = shepherd.isZcash(network.toLowerCase()) ? bitcoinZcash.address.fromBase58Check(address) : bitcoin.address.fromBase58Check(address);
if (_b58check.version === _network.pubKeyHash) {
return true;
} else {
return false;
}
} catch(e) {
return 'Invalid pub address';
}
};
shepherd.post('/electrum/keys', (req, res, next) => { shepherd.post('/electrum/keys', (req, res, next) => {
if (shepherd.checkToken(req.body.token)) {
let _matchingKeyPairs = 0; let _matchingKeyPairs = 0;
let _totalKeys = 0;
let _electrumKeys = {}; let _electrumKeys = {};
let _seed = req.body.seed;
for (let key in shepherd.electrumServers) { let _wifError = false;
const _abbr = shepherd.electrumServers[key].abbr;
const { priv, pub } = shepherd.seedToWif(req.body.seed, shepherd.findNetworkObj(_abbr), req.body.iguana); for (let key in shepherd.electrumCoins) {
if (key !== 'auth') {
const _abbr = key;
let isWif = false;
let priv;
let pub;
try {
bs58check.decode(_seed);
isWif = true;
} catch (e) {}
if (isWif) {
try {
let key = shepherd.isZcash(_abbr.toLowerCase()) ? bitcoinZcash.ECPair.fromWIF(_seed, shepherd.getNetworkData(_abbr.toLowerCase()), true) : bitcoin.ECPair.fromWIF(_seed, shepherd.getNetworkData(_abbr.toLowerCase()), true);
priv = key.toWIF();
pub = key.getAddress();
} catch (e) {
_wifError = true;
break;
}
} else {
let _keys = shepherd.seedToWif(_seed, shepherd.findNetworkObj(_abbr), req.body.iguana);
priv = _keys.priv;
pub = _keys.pub;
}
if (shepherd.electrumKeys[_abbr].pub === pub && if (shepherd.electrumKeys[_abbr].pub === pub &&
shepherd.electrumKeys[_abbr].priv === priv) { shepherd.electrumKeys[_abbr].priv === priv) {
_matchingKeyPairs++; _matchingKeyPairs++;
} }
_totalKeys++;
}
} }
if (req.body.active) { if (req.body.active) {
@ -82,21 +218,38 @@ module.exports = (shepherd) => {
_electrumKeys = shepherd.electrumKeys; _electrumKeys = shepherd.electrumKeys;
} }
// shepherd.log(JSON.stringify(_electrumKeys, null, '\t'), true);
const successObj = { const successObj = {
msg: 'success', msg: _wifError ? 'error' : 'success',
result: _matchingKeyPairs === Object.keys(shepherd.electrumKeys).length ? _electrumKeys : false, result: _wifError ? false : (_matchingKeyPairs === _totalKeys ? _electrumKeys : false),
}; };
res.end(JSON.stringify(successObj)); res.end(JSON.stringify(successObj));
} else {
const errorObj = {
msg: 'error',
result: 'unauthorized access',
};
res.end(JSON.stringify(errorObj));
}
}); });
shepherd.getSpvFees = () => {
let _fees = {};
for (let key in shepherd.electrumServers) {
if (shepherd.electrumServers[key].txfee) {
_fees[shepherd.electrumServers[key].abbr] = shepherd.electrumServers[key].txfee;
}
}
return _fees;
};
shepherd.post('/electrum/seed/bip39/match', (req, res, next) => { shepherd.post('/electrum/seed/bip39/match', (req, res, next) => {
const bip39 = require('bip39'); // npm i -S bip39 if (shepherd.checkToken(req.body.token)) {
const crypto = require('crypto');
const seed = bip39.mnemonicToSeed(req.body.seed); const seed = bip39.mnemonicToSeed(req.body.seed);
const hdMaster = shepherd.bitcoinJS.HDNode.fromSeedBuffer(seed, shepherd.electrumJSNetworks.komodo); // seed from above const hdMaster = bitcoin.HDNode.fromSeedBuffer(seed, shepherd.electrumJSNetworks.komodo);
const matchPattern = req.body.match; const matchPattern = req.body.match;
const _defaultAddressDepth = req.body.addressdepth; const _defaultAddressDepth = req.body.addressdepth;
const _defaultAccountCount = req.body.accounts; const _defaultAccountCount = req.body.accounts;
@ -114,10 +267,6 @@ module.exports = (shepherd) => {
priv: _key.keyPair.toWIF(), priv: _key.keyPair.toWIF(),
}; };
} }
/*_addresses.push({
pub: _key.keyPair.getAddress(),
priv: _key.keyPair.toWIF(),
});*/
} }
} }
} }
@ -128,51 +277,15 @@ module.exports = (shepherd) => {
}; };
res.end(JSON.stringify(successObj)); res.end(JSON.stringify(successObj));
}); } else {
const errorObj = {
// spv v2 msg: 'error',
/*shepherd.get('/electrum/bip39/seed', (req, res, next) => { result: 'unauthorized access',
const _seed = '';
// 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(_seed);
console.log(seed);
const successObj = {
msg: 'success',
result: {
servers: shepherd.electrumServers,
},
}; };
res.end(JSON.stringify(successObj)); res.end(JSON.stringify(errorObj));
}
console.log(shepherd.bitcoinJS.networks.komodo); });
const hdMaster = shepherd.bitcoinJS.HDNode.fromSeedBuffer(seed, shepherd.electrumJSNetworks.komodo); // seed from above
const key1 = hdMaster.derivePath("m/44'/141'/0'/0/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; return shepherd;
}; };

27
routes/shepherd/electrum/listunspent.js

@ -1,3 +1,5 @@
// TODO: watchonly spendable switch
module.exports = (shepherd) => { module.exports = (shepherd) => {
shepherd.listunspent = (ecl, address, network, full, verify) => { shepherd.listunspent = (ecl, address, network, full, verify) => {
let _atLeastOneDecodeTxFailed = false; let _atLeastOneDecodeTxFailed = false;
@ -24,11 +26,12 @@ module.exports = (shepherd) => {
} }
if (!_utxo.length) { // no confirmed utxo if (!_utxo.length) { // no confirmed utxo
ecl.close();
resolve('no valid utxo'); resolve('no valid utxo');
} else { } else {
shepherd.Promise.all(_utxo.map((_utxoItem, index) => { shepherd.Promise.all(_utxo.map((_utxoItem, index) => {
return new shepherd.Promise((resolve, reject) => { return new shepherd.Promise((resolve, reject) => {
ecl.blockchainTransactionGet(_utxoItem['tx_hash']) shepherd.getTransaction(_utxoItem['tx_hash'], network, ecl)
.then((_rawtxJSON) => { .then((_rawtxJSON) => {
shepherd.log('electrum gettransaction ==>', true); shepherd.log('electrum gettransaction ==>', true);
shepherd.log(index + ' | ' + (_rawtxJSON.length - 1), true); shepherd.log(index + ' | ' + (_rawtxJSON.length - 1), true);
@ -36,7 +39,7 @@ module.exports = (shepherd) => {
// decode tx // decode tx
const _network = shepherd.getNetworkData(network); const _network = shepherd.getNetworkData(network);
const decodedTx = shepherd.electrumJSTxDecoder(_rawtxJSON, _network); const decodedTx = shepherd.electrumJSTxDecoder(_rawtxJSON, network, _network);
shepherd.log('decoded tx =>', true); shepherd.log('decoded tx =>', true);
shepherd.log(decodedTx, true); shepherd.log(decodedTx, true);
@ -67,11 +70,12 @@ module.exports = (shepherd) => {
verified: false, verified: false,
}; };
// merkle root verification agains another electrum server // merkle root verification against another electrum server
if (verify) { if (verify) {
shepherd.verifyMerkleByCoin(shepherd.findCoinName(network), _utxoItem['tx_hash'], _utxoItem.height) shepherd.verifyMerkleByCoin(shepherd.findCoinName(network), _utxoItem['tx_hash'], _utxoItem.height)
.then((verifyMerkleRes) => { .then((verifyMerkleRes) => {
if (verifyMerkleRes && verifyMerkleRes === shepherd.CONNECTION_ERROR_OR_INCOMPLETE_DATA) { if (verifyMerkleRes &&
verifyMerkleRes === shepherd.CONNECTION_ERROR_OR_INCOMPLETE_DATA) {
verifyMerkleRes = false; verifyMerkleRes = false;
} }
@ -93,7 +97,7 @@ module.exports = (shepherd) => {
verified: false, verified: false,
}; };
// merkle root verification agains another electrum server // merkle root verification against another electrum server
if (verify) { if (verify) {
shepherd.verifyMerkleByCoin(shepherd.findCoinName(network), _utxoItem['tx_hash'], _utxoItem.height) shepherd.verifyMerkleByCoin(shepherd.findCoinName(network), _utxoItem['tx_hash'], _utxoItem.height)
.then((verifyMerkleRes) => { .then((verifyMerkleRes) => {
@ -126,6 +130,7 @@ module.exports = (shepherd) => {
}); });
} }
} else { } else {
ecl.close();
resolve('cant get current height'); resolve('cant get current height');
} }
}); });
@ -154,6 +159,7 @@ module.exports = (shepherd) => {
} }
shepherd.get('/electrum/listunspent', (req, res, next) => { shepherd.get('/electrum/listunspent', (req, res, next) => {
if (shepherd.checkToken(req.query.token)) {
const network = req.query.network || shepherd.findNetworkObj(req.query.coin); 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 ecl = new shepherd.electrumJSCore(shepherd.electrumServers[network].port, shepherd.electrumServers[network].address, shepherd.electrumServers[network].proto); // tcp or tls
@ -165,7 +171,8 @@ module.exports = (shepherd) => {
network, network,
true, true,
req.query.verify req.query.verify
).then((listunspent) => { )
.then((listunspent) => {
shepherd.log('electrum listunspent ==>', true); shepherd.log('electrum listunspent ==>', true);
const successObj = { const successObj = {
@ -189,6 +196,14 @@ module.exports = (shepherd) => {
res.end(JSON.stringify(successObj)); res.end(JSON.stringify(successObj));
}); });
} }
} else {
const errorObj = {
msg: 'error',
result: 'unauthorized access',
};
res.end(JSON.stringify(errorObj));
}
}); });
return shepherd; return shepherd;

30
routes/shepherd/electrum/merkle.js

@ -2,11 +2,11 @@ module.exports = (shepherd) => {
// get merkle root // get merkle root
shepherd.getMerkleRoot = (txid, proof, pos) => { shepherd.getMerkleRoot = (txid, proof, pos) => {
const reverse = require('buffer-reverse'); const reverse = require('buffer-reverse');
let hash = txid;
let serialized;
const _sha256 = (data) => { const _sha256 = (data) => {
return shepherd.crypto.createHash('sha256').update(data).digest(); return shepherd.crypto.createHash('sha256').update(data).digest();
} }
let hash = txid;
let serialized;
shepherd.log(`getMerkleRoot txid ${txid}`, true); shepherd.log(`getMerkleRoot txid ${txid}`, true);
shepherd.log(`getMerkleRoot pos ${pos}`, true); shepherd.log(`getMerkleRoot pos ${pos}`, true);
@ -30,7 +30,7 @@ module.exports = (shepherd) => {
return hash; return hash;
} }
shepherd.verifyMerkle = (txid, height, serverList, mainServer) => { shepherd.verifyMerkle = (txid, height, serverList, mainServer, network) => {
// select random server // select random server
const getRandomIntInclusive = (min, max) => { const getRandomIntInclusive = (min, max) => {
min = Math.ceil(min); min = Math.ceil(min);
@ -44,7 +44,7 @@ module.exports = (shepherd) => {
const _randomServer = randomServer.split(':'); const _randomServer = randomServer.split(':');
const _mainServer = mainServer.split(':'); const _mainServer = mainServer.split(':');
let ecl = new shepherd.electrumJSCore(_mainServer[1], _mainServer[0], 'tcp'); // tcp or tls let ecl = new shepherd.electrumJSCore(_mainServer[1], _mainServer[0], _mainServer[2]); // tcp or tls
return new shepherd.Promise((resolve, reject) => { return new shepherd.Promise((resolve, reject) => {
shepherd.log(`main server: ${mainServer}`, true); shepherd.log(`main server: ${mainServer}`, true);
@ -63,10 +63,10 @@ module.exports = (shepherd) => {
const _res = shepherd.getMerkleRoot(txid, merkleData.merkle, merkleData.pos); const _res = shepherd.getMerkleRoot(txid, merkleData.merkle, merkleData.pos);
shepherd.log(_res, true); shepherd.log(_res, true);
ecl = new shepherd.electrumJSCore(_randomServer[1], _randomServer[0], 'tcp'); ecl = new shepherd.electrumJSCore(_randomServer[1], _randomServer[0], _mainServer[2]);
ecl.connect(); ecl.connect();
ecl.blockchainBlockGetHeader(height) shepherd.getBlockHeader(height, network, ecl)
.then((blockInfo) => { .then((blockInfo) => {
if (blockInfo && if (blockInfo &&
blockInfo['merkle_root']) { blockInfo['merkle_root']) {
@ -83,13 +83,16 @@ module.exports = (shepherd) => {
resolve(false); resolve(false);
} }
} else { } else {
ecl.close();
resolve(shepherd.CONNECTION_ERROR_OR_INCOMPLETE_DATA); resolve(shepherd.CONNECTION_ERROR_OR_INCOMPLETE_DATA);
} }
} else { } else {
ecl.close();
resolve(shepherd.CONNECTION_ERROR_OR_INCOMPLETE_DATA); resolve(shepherd.CONNECTION_ERROR_OR_INCOMPLETE_DATA);
} }
}); });
} else { } else {
ecl.close();
resolve(shepherd.CONNECTION_ERROR_OR_INCOMPLETE_DATA); resolve(shepherd.CONNECTION_ERROR_OR_INCOMPLETE_DATA);
} }
}); });
@ -117,8 +120,10 @@ module.exports = (shepherd) => {
txid, txid,
height, height,
_filteredServerList, _filteredServerList,
shepherd.electrumCoins[coin].server.ip + ':' + shepherd.electrumCoins[coin].server.port shepherd.electrumCoins[coin].server.ip + ':' + shepherd.electrumCoins[coin].server.port + ':' + shepherd.electrumServers[coin === 'KMD' || coin === 'komodo' ? 'komodo' : coin.toLowerCase()].proto,
).then((proof) => { coin
)
.then((proof) => {
resolve(proof); resolve(proof);
}); });
} else { } else {
@ -128,6 +133,7 @@ module.exports = (shepherd) => {
} }
shepherd.get('/electrum/merkle/verify', (req, res, next) => { shepherd.get('/electrum/merkle/verify', (req, res, next) => {
if (shepherd.checkToken(req.query.token)) {
shepherd.verifyMerkleByCoin(req.query.coin, req.query.txid, req.query.height) shepherd.verifyMerkleByCoin(req.query.coin, req.query.txid, req.query.height)
.then((verifyMerkleRes) => { .then((verifyMerkleRes) => {
const successObj = { const successObj = {
@ -139,6 +145,14 @@ module.exports = (shepherd) => {
res.end(JSON.stringify(successObj)); res.end(JSON.stringify(successObj));
}); });
} else {
const errorObj = {
msg: 'error',
result: 'unauthorized access',
};
res.end(JSON.stringify(errorObj));
}
}); });
return shepherd; return shepherd;

81
routes/shepherd/electrum/network.js

@ -1,4 +1,42 @@
const txDecoder = {
default: require('../../electrumjs/electrumjs.txdecoder.js'),
zcash: require('../../electrumjs/electrumjs.txdecoder-2bytes.js'),
pos: require('../../electrumjs/electrumjs.txdecoder-pos.js'),
};
module.exports = (shepherd) => { module.exports = (shepherd) => {
shepherd.isZcash = (network) => {
if (network === 'ZEC' ||
network === 'zec' ||
network === 'zcash' ||
network === 'ZCASH' ||
network === 'HUSH' ||
network === 'hush' ||
network === 'ZCL' ||
network === 'zcl' ||
network === 'BTCZ' ||
network === 'btcz') {
return true;
}
};
shepherd.isPos = (network) => {
if (network === 'BLK' ||
network === 'blk') {
return true;
}
};
shepherd.electrumJSTxDecoder = (rawtx, networkName, network) => {
if (shepherd.isZcash(networkName)) {
return txDecoder.zcash(rawtx, network);
} else if (shepherd.isPos(networkName)) {
return txDecoder.pos(rawtx, network);
} else {
return txDecoder.default(rawtx, network);
}
};
shepherd.getNetworkData = (network) => { shepherd.getNetworkData = (network) => {
const coin = shepherd.findNetworkObj(network) || shepherd.findNetworkObj(network.toUpperCase()) || shepherd.findNetworkObj(network.toLowerCase()); const coin = shepherd.findNetworkObj(network) || shepherd.findNetworkObj(network.toUpperCase()) || shepherd.findNetworkObj(network.toLowerCase());
const coinUC = coin ? coin.toUpperCase() : null; const coinUC = coin ? coin.toUpperCase() : null;
@ -23,6 +61,12 @@ module.exports = (shepherd) => {
coin === 'MESH' || coin === 'MESH' ||
coin === 'WLC' || coin === 'WLC' ||
coin === 'MNZ' || coin === 'MNZ' ||
coin === 'BTCH' ||
coin === 'KMD' ||
coin === 'BEER' ||
coin === 'PIZZA' ||
coin === 'VOTE' ||
coin === 'KOMODO' ||
coinUC === 'SUPERNET' || coinUC === 'SUPERNET' ||
coinUC === 'REVS' || coinUC === 'REVS' ||
coinUC === 'SUPERNET' || coinUC === 'SUPERNET' ||
@ -42,7 +86,13 @@ module.exports = (shepherd) => {
coinUC === 'CEAL' || coinUC === 'CEAL' ||
coinUC === 'MESH' || coinUC === 'MESH' ||
coinUC === 'WLC' || coinUC === 'WLC' ||
coinUC === 'MNZ') { coinUC === 'MNZ' ||
coinUC === 'BTCH' ||
coinUC === 'BEER' ||
coinUC === 'PIZZA' ||
coinUC === 'VOTE' ||
coinUC === 'KMD' ||
coinUC === 'KOMODO') {
return shepherd.electrumJSNetworks.komodo; return shepherd.electrumJSNetworks.komodo;
} else { } else {
return shepherd.electrumJSNetworks[network]; return shepherd.electrumJSNetworks[network];
@ -58,6 +108,7 @@ module.exports = (shepherd) => {
} }
shepherd.get('/electrum/servers', (req, res, next) => { shepherd.get('/electrum/servers', (req, res, next) => {
if (shepherd.checkToken(req.query.token)) {
if (req.query.abbr) { if (req.query.abbr) {
let _electrumServers = {}; let _electrumServers = {};
@ -83,9 +134,18 @@ module.exports = (shepherd) => {
res.end(JSON.stringify(successObj)); res.end(JSON.stringify(successObj));
} }
} else {
const errorObj = {
msg: 'error',
result: 'unauthorized access',
};
res.end(JSON.stringify(errorObj));
}
}); });
shepherd.get('/electrum/coins/server/set', (req, res, next) => { shepherd.get('/electrum/coins/server/set', (req, res, next) => {
if (shepherd.checkToken(req.query.token)) {
shepherd.electrumCoins[req.query.coin].server = { shepherd.electrumCoins[req.query.coin].server = {
ip: req.query.address, ip: req.query.address,
port: req.query.port, port: req.query.port,
@ -99,7 +159,7 @@ module.exports = (shepherd) => {
} }
} }
shepherd.log(JSON.stringify(shepherd.electrumCoins[req.query.coin], null, '\t'), true); // shepherd.log(JSON.stringify(shepherd.electrumCoins[req.query.coin], null, '\t'), true);
const successObj = { const successObj = {
msg: 'success', msg: 'success',
@ -107,9 +167,18 @@ module.exports = (shepherd) => {
}; };
res.end(JSON.stringify(successObj)); res.end(JSON.stringify(successObj));
} else {
const errorObj = {
msg: 'error',
result: 'unauthorized access',
};
res.end(JSON.stringify(errorObj));
}
}); });
shepherd.get('/electrum/servers/test', (req, res, next) => { shepherd.get('/electrum/servers/test', (req, res, next) => {
if (shepherd.checkToken(req.query.token)) {
const ecl = new shepherd.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.connect();
@ -137,6 +206,14 @@ module.exports = (shepherd) => {
res.end(JSON.stringify(successObj)); res.end(JSON.stringify(successObj));
} }
}); });
} else {
const errorObj = {
msg: 'error',
result: 'unauthorized access',
};
res.end(JSON.stringify(errorObj));
}
}); });
return shepherd; return shepherd;

279
routes/shepherd/electrum/transactions.js

@ -1,3 +1,5 @@
const async = require('async');
module.exports = (shepherd) => { module.exports = (shepherd) => {
shepherd.sortTransactions = (transactions) => { shepherd.sortTransactions = (transactions) => {
return transactions.sort((b, a) => { return transactions.sort((b, a) => {
@ -13,7 +15,56 @@ module.exports = (shepherd) => {
}); });
} }
shepherd.getTransaction = (txid, network, ecl) => {
return new shepherd.Promise((resolve, reject) => {
if (!shepherd.electrumCache[network]) {
shepherd.electrumCache[network] = {};
}
if (!shepherd.electrumCache[network].tx) {
shepherd.electrumCache[network]['tx'] = {};
}
if (!shepherd.electrumCache[network].tx[txid]) {
shepherd.log(`electrum raw input tx ${txid}`, true);
ecl.blockchainTransactionGet(txid)
.then((_rawtxJSON) => {
shepherd.electrumCache[network].tx[txid] = _rawtxJSON;
resolve(_rawtxJSON);
});
} else {
shepherd.log(`electrum cached raw input tx ${txid}`, true);
resolve(shepherd.electrumCache[network].tx[txid]);
}
});
}
shepherd.getBlockHeader = (height, network, ecl) => {
return new shepherd.Promise((resolve, reject) => {
if (!shepherd.electrumCache[network]) {
shepherd.electrumCache[network] = {};
}
if (!shepherd.electrumCache[network].blockHeader) {
shepherd.electrumCache[network]['blockHeader'] = {};
}
if (!shepherd.electrumCache[network].blockHeader[height]) {
shepherd.log(`electrum raw block ${height}`, true);
ecl.blockchainBlockGetHeader(height)
.then((_rawtxJSON) => {
shepherd.electrumCache[network].blockHeader[height] = _rawtxJSON;
resolve(_rawtxJSON);
});
} else {
shepherd.log(`electrum cached raw block ${height}`, true);
resolve(shepherd.electrumCache[network].blockHeader[height]);
}
});
}
shepherd.get('/electrum/listtransactions', (req, res, next) => { shepherd.get('/electrum/listtransactions', (req, res, next) => {
if (shepherd.checkToken(req.query.token)) {
const network = req.query.network || shepherd.findNetworkObj(req.query.coin); 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 ecl = new shepherd.electrumJSCore(shepherd.electrumServers[network].port, shepherd.electrumServers[network].address, shepherd.electrumServers[network].proto); // tcp or tls
@ -49,58 +100,45 @@ module.exports = (shepherd) => {
.then((json) => { .then((json) => {
if (json && if (json &&
json.length) { json.length) {
let _rawtx = [];
json = shepherd.sortTransactions(json); json = shepherd.sortTransactions(json);
json = json.length > MAX_TX ? json.slice(0, MAX_TX) : json; json = json.length > MAX_TX ? json.slice(0, MAX_TX) : json;
let _rawtx = [];
shepherd.log(json.length, true); shepherd.log(json.length, true);
let index = 0;
shepherd.Promise.all(json.map((transaction, index) => { async.eachOfSeries(json, (transaction, ind, callback) => {
return new shepherd.Promise((resolve, reject) => { shepherd.getBlockHeader(transaction.height, network, ecl)
ecl.blockchainBlockGetHeader(transaction.height)
.then((blockInfo) => { .then((blockInfo) => {
if (blockInfo && if (blockInfo &&
blockInfo.timestamp) { blockInfo.timestamp) {
ecl.blockchainTransactionGet(transaction['tx_hash']) shepherd.getTransaction(transaction['tx_hash'], network, ecl)
.then((_rawtxJSON) => { .then((_rawtxJSON) => {
shepherd.log('electrum gettransaction ==>', true); shepherd.log('electrum gettransaction ==>', true);
shepherd.log((index + ' | ' + (_rawtxJSON.length - 1)), true); shepherd.log((index + ' | ' + (_rawtxJSON.length - 1)), true);
shepherd.log(_rawtxJSON, true); // shepherd.log(_rawtxJSON, true);
// decode tx // decode tx
const _network = shepherd.getNetworkData(network); const _network = shepherd.getNetworkData(network);
const decodedTx = shepherd.electrumJSTxDecoder(_rawtxJSON, _network); const decodedTx = shepherd.electrumJSTxDecoder(_rawtxJSON, network, _network);
let txInputs = []; let txInputs = [];
shepherd.log(`decodedtx network ${network}`, true);
shepherd.log('decodedtx =>', true); shepherd.log('decodedtx =>', true);
shepherd.log(decodedTx.outputs, true); // shepherd.log(decodedTx.outputs, true);
let index2 = 0;
if (decodedTx && if (decodedTx &&
decodedTx.inputs) { decodedTx.inputs &&
shepherd.Promise.all(decodedTx.inputs.map((_decodedInput, index) => { decodedTx.inputs.length) {
return new shepherd.Promise((_resolve, _reject) => { async.eachOfSeries(decodedTx.inputs, (_decodedInput, ind2, callback2) => {
if (_decodedInput.txid !== '0000000000000000000000000000000000000000000000000000000000000000') { function checkLoop() {
ecl.blockchainTransactionGet(_decodedInput.txid) index2++;
.then((rawInput) => {
const decodedVinVout = shepherd.electrumJSTxDecoder(rawInput, _network); if (index2 === decodedTx.inputs.length) {
shepherd.log(`tx history decode inputs ${decodedTx.inputs.length} | ${index2} => main callback`, true);
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 = { const _parsedTx = {
network: decodedTx.network, network: decodedTx.network,
format: decodedTx.format, format: decodedTx.format,
@ -140,7 +178,39 @@ module.exports = (shepherd) => {
_rawtx.push(formattedTx[0]); _rawtx.push(formattedTx[0]);
_rawtx.push(formattedTx[1]); _rawtx.push(formattedTx[1]);
} }
resolve(true); index++;
if (index === json.length) {
ecl.close();
const successObj = {
msg: 'success',
result: _rawtx,
};
res.end(JSON.stringify(successObj));
}
callback();
shepherd.log(`tx history main loop ${json.length} | ${index}`, true);
}
callback2();
}
if (_decodedInput.txid !== '0000000000000000000000000000000000000000000000000000000000000000') {
shepherd.getTransaction(_decodedInput.txid, network, ecl)
.then((rawInput) => {
const decodedVinVout = shepherd.electrumJSTxDecoder(rawInput, network, _network);
if (decodedVinVout) {
shepherd.log(decodedVinVout.outputs[_decodedInput.n], true);
txInputs.push(decodedVinVout.outputs[_decodedInput.n]);
}
checkLoop();
});
} else {
checkLoop();
}
}); });
} else { } else {
const _parsedTx = { const _parsedTx = {
@ -155,7 +225,20 @@ module.exports = (shepherd) => {
const formattedTx = shepherd.parseTransactionAddresses(_parsedTx, req.query.address, network); const formattedTx = shepherd.parseTransactionAddresses(_parsedTx, req.query.address, network);
_rawtx.push(formattedTx); _rawtx.push(formattedTx);
resolve(true); index++;
if (index === json.length) {
ecl.close();
const successObj = {
msg: 'success',
result: _rawtx,
};
res.end(JSON.stringify(successObj));
} else {
callback();
}
} }
}); });
} else { } else {
@ -170,12 +253,9 @@ module.exports = (shepherd) => {
}; };
const formattedTx = shepherd.parseTransactionAddresses(_parsedTx, req.query.address, network); const formattedTx = shepherd.parseTransactionAddresses(_parsedTx, req.query.address, network);
_rawtx.push(formattedTx); _rawtx.push(formattedTx);
resolve(true); index++;
}
}); if (index === json.length) {
});
}))
.then(promiseResult => {
ecl.close(); ecl.close();
const successObj = { const successObj = {
@ -184,8 +264,15 @@ module.exports = (shepherd) => {
}; };
res.end(JSON.stringify(successObj)); res.end(JSON.stringify(successObj));
} else {
callback();
}
}
});
}); });
} else { } else {
ecl.close();
const successObj = { const successObj = {
msg: 'success', msg: 'success',
result: [], result: [],
@ -204,9 +291,18 @@ module.exports = (shepherd) => {
} }
}); });
} }
} else {
const errorObj = {
msg: 'error',
result: 'unauthorized access',
};
res.end(JSON.stringify(errorObj));
}
}); });
shepherd.get('/electrum/gettransaction', (req, res, next) => { shepherd.get('/electrum/gettransaction', (req, res, next) => {
if (shepherd.checkToken(req.query.token)) {
const network = req.query.network || shepherd.findNetworkObj(req.query.coin); 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 ecl = new shepherd.electrumJSCore(shepherd.electrumServers[network].port, shepherd.electrumServers[network].address, shepherd.electrumServers[network].proto); // tcp or tls
@ -225,12 +321,21 @@ module.exports = (shepherd) => {
res.end(JSON.stringify(successObj)); res.end(JSON.stringify(successObj));
}); });
} else {
const errorObj = {
msg: 'error',
result: 'unauthorized access',
};
res.end(JSON.stringify(errorObj));
}
}); });
shepherd.parseTransactionAddresses = (tx, targetAddress, network) => { shepherd.parseTransactionAddresses = (tx, targetAddress, network, skipTargetAddress) => {
// TODO: - sum vins / sum vouts to the same address // TODO: - sum vins / sum vouts to the same address
// - multi vin multi vout // - multi vin multi vout
// - detect change address // - detect change address
// - double check for exact sum input/output values
let result = []; let result = [];
let _parse = { let _parse = {
inputs: {}, inputs: {},
@ -244,6 +349,10 @@ module.exports = (shepherd) => {
inputs: 0, inputs: 0,
outputs: 0, outputs: 0,
}; };
let _addresses = {
inputs: [],
outputs: [],
};
shepherd.log('parseTransactionAddresses result ==>', true); shepherd.log('parseTransactionAddresses result ==>', true);
@ -273,25 +382,82 @@ module.exports = (shepherd) => {
_total[key] += Number(_parse[key][i].value); _total[key] += Number(_parse[key][i].value);
// ignore op return outputs
if (_parse[key][i].scriptPubKey && if (_parse[key][i].scriptPubKey &&
_parse[key][i].scriptPubKey.addresses && _parse[key][i].scriptPubKey.addresses &&
_parse[key][i].scriptPubKey.addresses[0] &&
_parse[key][i].scriptPubKey.addresses[0] === targetAddress && _parse[key][i].scriptPubKey.addresses[0] === targetAddress &&
_parse[key][i].value) { _parse[key][i].value) {
_sum[key] += Number(_parse[key][i].value); _sum[key] += Number(_parse[key][i].value);
} }
if (_parse[key][i].scriptPubKey &&
_parse[key][i].scriptPubKey.addresses &&
_parse[key][i].scriptPubKey.addresses[0]) {
_addresses[key].push(_parse[key][i].scriptPubKey.addresses[0]);
if (_parse[key][i].scriptPubKey.addresses[0] === targetAddress &&
skipTargetAddress) {
_addresses[key].pop();
}
}
}
}
_addresses.inputs = [ ...new Set(_addresses.inputs) ];
_addresses.outputs = [ ...new Set(_addresses.outputs) ];
shepherd.log('addresses in =>', true);
shepherd.log(_addresses.inputs, true);
shepherd.log('addresses out =>', true);
shepherd.log(_addresses.outputs, true);
let isSelfSend = {
inputs: false,
outputs: false,
};
for (let key in _parse) {
for (let i = 0; i < _addresses[key].length; i++) {
if (_addresses[key][i] === targetAddress &&
_addresses[key].length === 1) {
isSelfSend[key] = true;
}
} }
} }
if (_sum.inputs > 0 && if (_sum.inputs > 0 &&
_sum.outputs > 0) { _sum.outputs > 0) {
// vin + change, break into two tx // vin + change, break into two tx
// send to self
if (isSelfSend.inputs && isSelfSend.outputs) {
result = {
type: 'self',
amount: Number(_sum.inputs - _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.interest = Number(vinVoutDiff.toFixed(8));
}
}
} else {
result = [{ // reorder since tx sort by default is from newest to oldest result = [{ // reorder since tx sort by default is from newest to oldest
type: 'sent', type: 'sent',
amount: Number(_sum.inputs.toFixed(8)), amount: Number(_sum.inputs.toFixed(8)),
address: targetAddress, address: _addresses.outputs[0],
timestamp: tx.timestamp, timestamp: tx.timestamp,
txid: tx.format.txid, txid: tx.format.txid,
confirmations: tx.confirmations, confirmations: tx.confirmations,
from: _addresses.inputs,
to: _addresses.outputs,
}, { }, {
type: 'received', type: 'received',
amount: Number(_sum.outputs.toFixed(8)), amount: Number(_sum.outputs.toFixed(8)),
@ -299,6 +465,8 @@ module.exports = (shepherd) => {
timestamp: tx.timestamp, timestamp: tx.timestamp,
txid: tx.format.txid, txid: tx.format.txid,
confirmations: tx.confirmations, confirmations: tx.confirmations,
from: _addresses.inputs,
to: _addresses.outputs,
}]; }];
if (network === 'komodo') { // calc claimed interest amount if (network === 'komodo') { // calc claimed interest amount
@ -308,6 +476,7 @@ module.exports = (shepherd) => {
result[1].interest = Number(vinVoutDiff.toFixed(8)); result[1].interest = Number(vinVoutDiff.toFixed(8));
} }
} }
}
} else if (_sum.inputs === 0 && _sum.outputs > 0) { } else if (_sum.inputs === 0 && _sum.outputs > 0) {
result = { result = {
type: 'received', type: 'received',
@ -316,22 +485,26 @@ module.exports = (shepherd) => {
timestamp: tx.timestamp, timestamp: tx.timestamp,
txid: tx.format.txid, txid: tx.format.txid,
confirmations: tx.confirmations, confirmations: tx.confirmations,
from: _addresses.inputs,
to: _addresses.outputs,
}; };
} else if (_sum.inputs > 0 && _sum.outputs === 0) { } else if (_sum.inputs > 0 && _sum.outputs === 0) {
result = { result = {
type: 'sent', type: 'sent',
amount: Number(_sum.inputs.toFixed(8)), amount: Number(_sum.inputs.toFixed(8)),
address: targetAddress, address: isSelfSend.inputs && isSelfSend.outputs ? targetAddress : _addresses.outputs[0],
timestamp: tx.timestamp, timestamp: tx.timestamp,
txid: tx.format.txid, txid: tx.format.txid,
confirmations: tx.confirmations, confirmations: tx.confirmations,
from: _addresses.inputs,
to: _addresses.outputs,
}; };
} else { } else {
// (?) // (?)
result = { result = {
type: 'other', type: 'other',
amount: 'unknown', amount: 'unknown',
address: targetAddress, address: 'unknown',
timestamp: tx.timestamp, timestamp: tx.timestamp,
txid: tx.format.txid, txid: tx.format.txid,
confirmations: tx.confirmations, confirmations: tx.confirmations,
@ -345,10 +518,11 @@ module.exports = (shepherd) => {
} }
shepherd.get('/electrum/decoderawtx', (req, res, next) => { shepherd.get('/electrum/decoderawtx', (req, res, next) => {
if (shepherd.checkToken(req.query.token)) {
const _network = shepherd.getNetworkData(req.query.network); const _network = shepherd.getNetworkData(req.query.network);
const _rawtx = req.query.rawtx; const _rawtx = req.query.rawtx;
// const _rawtx = '0100000001dd6d064f5665f8454293ecaa9dbb55accf4f7e443d35f3b5ab7760f54b6c15fe000000006a473044022056355585a4a501ec9afc96aa5df124cf29ad3ac6454b47cd07cd7d89ec95ec2b022074c4604ee349d30e5336f210598e4dc576bf16ebeb67eeac3f4e82f56e930fee012103b90ba01af308757054e0484bb578765d5df59c4a57adbb94e2419df5e7232a63feffffff0289fc923b000000001976a91424af38fcb13bbc171b0b42bb017244a53b6bb2fa88ac20a10700000000001976a9142f4c0f91fc06ac228c120aee41741d0d3909683288ac49258b58'; //const _rawtx = '010000006f2c395a02d81487fc7f9d1be3ea900316730133c044af70cd76d21e988e71de0e9e85918f010000006a47304402202097acd391e1d0eaaf91844bd596e918fb71320e3e0c51554acb71a39e4ee98b0220548fd61d4ae77a08d70b01bf5340983a1ba63f6b71ad71d478af77011f96fd510121031ffc010d8abc4180b4c1a13962bf9153a78082e7f2ac18f7d14cb6a6634ca218feffffff2b31f6c9a7916f7cf128cae94b3fc10e4c74ca3a740e1a7a6fd6624e4e9a5c8b010000006a473044022063f014c5fbaa7614732e0ae486179a854215fc32c02230e13f69b7e81fa000e50220236a2ba6373b1854aafc59c5391ab7505062067f3d293c016cbb5d252b35a56a012102f307f17d282fc0eabf99227c2e0f3122ae9ecd7da0de099f0c6007d4c941b57bfeffffff021b797ad7120000001976a914c7a7142d743b3e6eebe76923f43bae477d3ce31a88acff086d66000000001976a91463800ff36b9c52b2ffe5564af1c2a38df4f0126788ac16381d00';
const decodedTx = shepherd.electrumJSTxDecoder(_rawtx, _network); const decodedTx = shepherd.electrumJSTxDecoder(_rawtx, req.query.network, _network);
shepherd.log('electrum decoderawtx input tx ==>', true); shepherd.log('electrum decoderawtx input tx ==>', true);
@ -370,13 +544,16 @@ module.exports = (shepherd) => {
} else { } 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 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
shepherd.log(decodedTx.inputs[0]);
shepherd.log(decodedTx.inputs[0].txid);
ecl.connect(); ecl.connect();
ecl.blockchainTransactionGet(decodedTx.inputs[0].txid) ecl.blockchainTransactionGet(decodedTx.inputs[0].txid)
.then((json) => { .then((json) => {
ecl.close(); ecl.close();
shepherd.log(json, true); shepherd.log(json, true);
const decodedVin = shepherd.electrumJSTxDecoder(json, _network); const decodedVin = shepherd.electrumJSTxDecoder(json, req.query.network, _network);
const successObj = { const successObj = {
msg: 'success', msg: 'success',
@ -391,6 +568,14 @@ module.exports = (shepherd) => {
res.end(JSON.stringify(successObj)); res.end(JSON.stringify(successObj));
}); });
} }
} else {
const errorObj = {
msg: 'error',
result: 'unauthorized access',
};
res.end(JSON.stringify(errorObj));
}
}); });
return shepherd; return shepherd;

9
routes/shepherd/kickstart.js

@ -7,6 +7,7 @@ module.exports = (shepherd) => {
* params: coin, type * params: coin, type
*/ */
shepherd.get('/kick', (req, res, next) => { shepherd.get('/kick', (req, res, next) => {
if (shepherd.checkToken(req.query.token)) {
const _coin = req.query.coin; const _coin = req.query.coin;
const _keepWallet = req.query.keepwallet; const _keepWallet = req.query.keepwallet;
@ -39,6 +40,14 @@ module.exports = (shepherd) => {
res.end(JSON.stringify(successObj)); res.end(JSON.stringify(successObj));
} }
} else {
const errorObj = {
msg: 'error',
result: 'unauthorized access',
};
res.end(JSON.stringify(errorObj));
}
}); });
return shepherd; return shepherd;

27
routes/shepherd/log.js

@ -40,12 +40,21 @@ module.exports = (shepherd) => {
} }
shepherd.get('/log/runtime', (req, res, next) => { shepherd.get('/log/runtime', (req, res, next) => {
if (shepherd.checkToken(req.query.token)) {
const successObj = { const successObj = {
msg: 'success', msg: 'success',
result: req.query.spv && req.query.spv === 'true' ? shepherd.appRuntimeSPVLog : shepherd.appRuntimeLog, result: req.query.spv && req.query.spv === 'true' ? shepherd.appRuntimeSPVLog : shepherd.appRuntimeLog,
}; };
res.end(JSON.stringify(successObj)); res.end(JSON.stringify(successObj));
} else {
const errorObj = {
msg: 'error',
result: 'unauthorized access',
};
res.end(JSON.stringify(errorObj));
}
}); });
shepherd.getAppRuntimeLog = () => { shepherd.getAppRuntimeLog = () => {
@ -59,6 +68,7 @@ module.exports = (shepherd) => {
* params: payload * params: payload
*/ */
shepherd.post('/guilog', (req, res, next) => { shepherd.post('/guilog', (req, res, next) => {
if (shepherd.checkToken(req.body.token)) {
const logLocation = `${shepherd.agamaDir}/shepherd`; const logLocation = `${shepherd.agamaDir}/shepherd`;
if (!shepherd.guiLog[shepherd.appSessionHash]) { if (!shepherd.guiLog[shepherd.appSessionHash]) {
@ -90,6 +100,14 @@ module.exports = (shepherd) => {
res.end(JSON.stringify(returnObj)); res.end(JSON.stringify(returnObj));
}); });
} else {
const errorObj = {
msg: 'error',
result: 'unauthorized access',
};
res.end(JSON.stringify(errorObj));
}
}); });
/* /*
@ -97,6 +115,7 @@ module.exports = (shepherd) => {
* params: type * params: type
*/ */
shepherd.get('/getlog', (req, res, next) => { shepherd.get('/getlog', (req, res, next) => {
if (shepherd.checkToken(req.query.token)) {
const logExt = req.query.type === 'txt' ? 'txt' : 'json'; const logExt = req.query.type === 'txt' ? 'txt' : 'json';
if (shepherd.fs.existsSync(`${shepherd.agamaDir}/shepherd/agamalog.${logExt}`)) { if (shepherd.fs.existsSync(`${shepherd.agamaDir}/shepherd/agamalog.${logExt}`)) {
@ -123,6 +142,14 @@ module.exports = (shepherd) => {
result: `agama.${logExt} doesnt exist`, result: `agama.${logExt} doesnt exist`,
}; };
res.end(JSON.stringify(errorObj));
}
} else {
const errorObj = {
msg: 'error',
result: 'unauthorized access',
};
res.end(JSON.stringify(errorObj)); res.end(JSON.stringify(errorObj));
} }
}); });

35
routes/shepherd/pin.js

@ -7,6 +7,7 @@ module.exports = (shepherd) => {
* params: none * params: none
*/ */
shepherd.post('/encryptkey', (req, res, next) => { shepherd.post('/encryptkey', (req, res, next) => {
if (shepherd.checkToken(req.body.token)) {
if (req.body.key && if (req.body.key &&
req.body.string && req.body.string &&
req.body.pubkey) { req.body.pubkey) {
@ -34,15 +35,15 @@ module.exports = (shepherd) => {
res.end(JSON.stringify(returnObj)); res.end(JSON.stringify(returnObj));
}); });
} else { } else {
let errorObj = {
msg: 'error',
result: '',
};
const _paramsList = [ const _paramsList = [
'key', 'key',
'string', 'string',
'pubkey' 'pubkey'
]; ];
let errorObj = {
msg: 'error',
result: '',
};
let _errorParamsList = []; let _errorParamsList = [];
for (let i = 0; i < _paramsList.length; i++) { for (let i = 0; i < _paramsList.length; i++) {
@ -52,11 +53,20 @@ module.exports = (shepherd) => {
} }
errorObj.result = `missing param ${_errorParamsList.join(', ')}`; errorObj.result = `missing param ${_errorParamsList.join(', ')}`;
res.end(JSON.stringify(errorObj));
}
} else {
const errorObj = {
msg: 'error',
result: 'unauthorized access',
};
res.end(JSON.stringify(errorObj)); res.end(JSON.stringify(errorObj));
} }
}); });
shepherd.post('/decryptkey', (req, res, next) => { shepherd.post('/decryptkey', (req, res, next) => {
if (shepherd.checkToken(req.body.token)) {
if (req.body.key && if (req.body.key &&
req.body.pubkey) { req.body.pubkey) {
if (fs.existsSync(`${shepherd.agamaDir}/shepherd/pin/${req.body.pubkey}.pin`)) { if (fs.existsSync(`${shepherd.agamaDir}/shepherd/pin/${req.body.pubkey}.pin`)) {
@ -104,11 +114,20 @@ module.exports = (shepherd) => {
result: 'missing key or pubkey param', result: 'missing key or pubkey param',
}; };
res.end(JSON.stringify(errorObj));
}
} else {
const errorObj = {
msg: 'error',
result: 'unauthorized access',
};
res.end(JSON.stringify(errorObj)); res.end(JSON.stringify(errorObj));
} }
}); });
shepherd.get('/getpinlist', (req, res, next) => { shepherd.get('/getpinlist', (req, res, next) => {
if (shepherd.checkToken(req.body.token)) {
if (fs.existsSync(`${shepherd.agamaDir}/shepherd/pin`)) { if (fs.existsSync(`${shepherd.agamaDir}/shepherd/pin`)) {
fs.readdir(`${shepherd.agamaDir}/shepherd/pin`, (err, items) => { fs.readdir(`${shepherd.agamaDir}/shepherd/pin`, (err, items) => {
let _pins = []; let _pins = [];
@ -141,6 +160,14 @@ module.exports = (shepherd) => {
result: 'pin folder doesnt exist', result: 'pin folder doesnt exist',
}; };
res.end(JSON.stringify(errorObj));
}
} else {
const errorObj = {
msg: 'error',
result: 'unauthorized access',
};
res.end(JSON.stringify(errorObj)); res.end(JSON.stringify(errorObj));
} }
}); });

18
routes/shepherd/quitDaemon.js

@ -82,6 +82,7 @@ module.exports = (shepherd) => {
} }
shepherd.post('/coind/stop', (req, res) => { shepherd.post('/coind/stop', (req, res) => {
if (shepherd.checkToken(req.body.token)) {
const _chain = req.body.chain; const _chain = req.body.chain;
let _coindQuitCmd = shepherd.komodocliBin; let _coindQuitCmd = shepherd.komodocliBin;
let _arg = []; let _arg = [];
@ -135,9 +136,18 @@ module.exports = (shepherd) => {
} }
} }
}); });
} else {
const errorObj = {
msg: 'error',
result: 'unauthorized access',
};
res.end(JSON.stringify(errorObj));
}
}); });
shepherd.post('/coins/remove', (req, res) => { shepherd.post('/coins/remove', (req, res) => {
if (shepherd.checkToken(req.body.token)) {
const _chain = req.body.chain; const _chain = req.body.chain;
if (req.body.mode === 'native') { if (req.body.mode === 'native') {
@ -164,6 +174,14 @@ module.exports = (shepherd) => {
res.end(JSON.stringify(obj)); res.end(JSON.stringify(obj));
} }
} else {
const errorObj = {
msg: 'error',
result: 'unauthorized access',
};
res.end(JSON.stringify(errorObj));
}
}); });
return shepherd; return shepherd;

121
routes/shepherd/rpc.js

@ -60,6 +60,7 @@ module.exports = (shepherd) => {
* params: payload * params: payload
*/ */
shepherd.post('/cli', (req, res, next) => { shepherd.post('/cli', (req, res, next) => {
if (shepherd.checkToken(req.body.payload.token)) {
if (!req.body.payload) { if (!req.body.payload) {
const errorObj = { const errorObj = {
msg: 'error', msg: 'error',
@ -77,7 +78,7 @@ module.exports = (shepherd) => {
} else { } else {
const _mode = req.body.payload.mode === 'passthru' ? 'passthru' : 'default'; const _mode = req.body.payload.mode === 'passthru' ? 'passthru' : 'default';
const _chain = req.body.payload.chain === 'KMD' ? null : req.body.payload.chain; const _chain = req.body.payload.chain === 'KMD' ? null : req.body.payload.chain;
const _params = req.body.payload.params ? ` ${req.body.payload.params}` : ''; let _params = req.body.payload.params ? ` ${req.body.payload.params}` : '';
let _cmd = req.body.payload.cmd; let _cmd = req.body.payload.cmd;
if (!shepherd.rpcConf[_chain]) { if (!shepherd.rpcConf[_chain]) {
@ -85,6 +86,115 @@ module.exports = (shepherd) => {
} }
if (_mode === 'default') { if (_mode === 'default') {
if (req.body.payload.rpc2cli) {
let _coindCliBin = shepherd.komodocliBin;
if (shepherd.nativeCoindList &&
_chain &&
shepherd.nativeCoindList[_chain.toLowerCase()]) {
_coindCliBin = `${shepherd.coindRootDir}/${_chain.toLowerCase()}/${shepherd.nativeCoindList[_chain.toLowerCase()].bin.toLowerCase()}-cli`;
}
if (_params.indexOf('*')) {
_params = _params.replace('*', '"*"');
}
if (_params.indexOf(',') > -1) {
_params = _params.split(',');
}
if (_cmd.indexOf('getaddressesbyaccount') > -1) {
_cmd = 'getaddressesbyaccount ""';
}
let _arg = (_chain ? ' -ac_name=' + _chain : '') + ' ' + _cmd + (typeof _params === 'object' ? _params.join(' ') : _params);
if (shepherd.appConfig.dataDir.length) {
_arg = `${_arg} -datadir=${shepherd.appConfig.dataDir + (_chain ? '/' + key : '')}`;
}
shepherd.exec(`"${_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) {
let _res;
let _error;
if (_chain !== 'komodod' &&
stderr.indexOf(`error creating`) > -1) {
shepherd.log(`replace error creating (gen${_chain})`);
stderr = stderr.replace(`error creating (gen${_chain})`, '');
shepherd.log(stderr);
}
if ((stderr.indexOf('{') > -1 && stderr.indexOf('}') > -1) ||
(stderr.indexOf('[') > -1 && stderr.indexOf(']') > -1)) {
_res = JSON.parse(stderr);
} else {
_res = stderr.trim();
}
if (stderr.indexOf('error code:') > -1) {
_error = {
code: Number(stderr.substring(stderr.indexOf('error code:') + 11, stderr.indexOf('error message:') - stderr.indexOf('error code:')).trim()),
message: stderr.substring(stderr.indexOf('error message:') + 15, stderr.length).trim(),
};
}
if (_error) {
responseObj = {
error: _error,
};
} else {
responseObj = {
result: _res,
};
}
} else {
let _res;
let _error;
if (_chain !== 'komodod' &&
stdout.indexOf(`error creating`) > -1) {
shepherd.log(`replace error creating (gen${_chain})`);
stdout = stdout.replace(`error creating (gen${_chain})`, '');
shepherd.log(stdout);
}
if ((stdout.indexOf('{') > -1 && stdout.indexOf('}') > -1) ||
(stdout.indexOf('[') > -1 && stdout.indexOf(']') > -1)) {
_res = JSON.parse(stdout);
} else {
_res = stdout.trim();
}
if (stdout.indexOf('error code:') > -1) {
_error = {
code: Number(stdout.substring(stdout.indexOf('error code:') + 11, stdout.indexOf('error message:') - stdout.indexOf('error code:')).trim()),
message: stdout.substring(stdout.indexOf('error message:') + 15, stdout.length).trim(),
};
}
if (_error) {
responseObj = {
error: _error,
};
} else {
responseObj = {
result: _res,
};
}
}
res.end(JSON.stringify(responseObj));
// shepherd.killRogueProcess('komodo-cli');
});
} else {
if (_cmd === 'debug' && if (_cmd === 'debug' &&
_chain !== 'CHIPS') { _chain !== 'CHIPS') {
if (shepherd.nativeCoindList[_chain.toLowerCase()]) { if (shepherd.nativeCoindList[_chain.toLowerCase()]) {
@ -172,6 +282,7 @@ module.exports = (shepherd) => {
}); });
} }
} }
}
} else { } else {
let _coindCliBin = shepherd.komodocliBin; let _coindCliBin = shepherd.komodocliBin;
@ -215,6 +326,14 @@ module.exports = (shepherd) => {
}); });
} }
} }
} else {
const errorObj = {
msg: 'error',
result: 'unauthorized access',
};
res.end(JSON.stringify(errorObj));
}
}); });
return shepherd; return shepherd;

6
version

@ -1,3 +1,3 @@
version=0.2.0.25e version=0.2.0.29c
type=e-beta type=c-beta
minversion=0.2.0.2 minversion=0.2.0.29

2
version_build

@ -1 +1 @@
0.2.0.25e-beta 0.2.0.29c-beta
Loading…
Cancel
Save