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. 504
      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. 461
      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. 152
      routes/shepherd/addCoinShortcuts.js
  15. 27
      routes/shepherd/appInfo.js
  16. 48
      routes/shepherd/auth.js
  17. 239
      routes/shepherd/binsTestUtil.js
  18. 141
      routes/shepherd/coindWalletKeys.js
  19. 41
      routes/shepherd/coins.js
  20. 94
      routes/shepherd/coinsList.js
  21. 65
      routes/shepherd/config.js
  22. 280
      routes/shepherd/daemonControl.js
  23. 518
      routes/shepherd/dashboardUpdate.js
  24. 122
      routes/shepherd/debugLog.js
  25. 208
      routes/shepherd/downloadBins.js
  26. 147
      routes/shepherd/downloadPatch.js
  27. 107
      routes/shepherd/downloadZcparams.js
  28. 372
      routes/shepherd/elections.js
  29. 139
      routes/shepherd/electrum/auth.js
  30. 203
      routes/shepherd/electrum/balance.js
  31. 46
      routes/shepherd/electrum/block.js
  32. 81
      routes/shepherd/electrum/coins.js
  33. 500
      routes/shepherd/electrum/createtx-multi.js
  34. 86
      routes/shepherd/electrum/createtx-split.js
  35. 792
      routes/shepherd/electrum/createtx.js
  36. 31
      routes/shepherd/electrum/estimate.js
  37. 2
      routes/shepherd/electrum/interest.js
  38. 353
      routes/shepherd/electrum/keys.js
  39. 89
      routes/shepherd/electrum/listunspent.js
  40. 48
      routes/shepherd/electrum/merkle.js
  41. 199
      routes/shepherd/electrum/network.js
  42. 573
      routes/shepherd/electrum/transactions.js
  43. 59
      routes/shepherd/kickstart.js
  44. 127
      routes/shepherd/log.js
  45. 209
      routes/shepherd/pin.js
  46. 132
      routes/shepherd/quitDaemon.js
  47. 373
      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

504
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()}`);
shepherd.log(`app init ${appSessionHash}`); if (process.argv.indexOf('devmode') > -1) {
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,191 +222,242 @@ function createWindow(status, hideLoadingWindow) {
{ role: 'selectall' }, { role: 'selectall' },
]); ]);
// load our index.html (i.e. easyDEX GUI) // check if agama is already running
shepherd.writeLog('show edex gui'); portscanner.checkPortStatus(appConfig.agamaPort, '127.0.0.1', (error, status) => {
mainWindow.appConfig = appConfig; // Status is 'open' if currently in use or 'closed' if available
mainWindow.appConfigSchema = shepherd.appConfigSchema; if (status === 'closed') {
mainWindow.arch = arch(); server.listen(appConfig.agamaPort, () => {
mainWindow.appBasicInfo = appBasicInfo; shepherd.log(`guiapp and sockets.io are listening on port ${appConfig.agamaPort}`);
mainWindow.appSessionHash = appSessionHash; shepherd.writeLog(`guiapp and sockets.io are listening on port ${appConfig.agamaPort}`);
mainWindow.assetChainPorts = require('./routes/ports.js'); // start sockets.io
mainWindow.agamaIcon = agamaIcon; io.set('origins', appConfig.dev ? 'http://127.0.0.1:3000' : `http://127.0.0.1:${appConfig.agamaPort}`); // set origin
mainWindow.testLocation = shepherd.testLocation; });
mainWindow.kmdMainPassiveMode = shepherd.kmdMainPassiveMode;
mainWindow.getAppRuntimeLog = shepherd.getAppRuntimeLog;
mainWindow.nativeCoindList = nativeCoindList;
mainWindow.zcashParamsExist = _zcashParamsExist;
mainWindow.zcashParamsExistPromise = shepherd.zcashParamsExistPromise;
mainWindow.zcashParamsDownloadLinks = shepherd.zcashParamsDownloadLinks;
mainWindow.isWindows = os.platform() === 'win32' ? true : false; // obsolete(?)
mainWindow.appExit = appExit;
mainWindow.getMaxconKMDConf = shepherd.getMaxconKMDConf;
mainWindow.setMaxconKMDConf = shepherd.setMaxconKMDConf;
mainWindow.getMMCacheData = shepherd.getMMCacheData;
mainWindow.activeSection = 'wallets';
mainWindow.argv = process.argv;
mainWindow.getAssetChainPorts = shepherd.getAssetChainPorts;
if (appConfig.dev) {
mainWindow.loadURL('http://127.0.0.1:3000');
} else {
mainWindow.loadURL(`http://${appConfig.host}:${appConfig.agamaPort}/gui/EasyDEX-GUI/react/build`);
}
mainWindow.webContents.on('did-finish-load', () => { // initialise window
setTimeout(() => { mainWindow = new BrowserWindow({ // dirty hack to prevent main window flash on quit
mainWindow.show(); width: closeAppAfterLoading ? 1 : 1280,
height: closeAppAfterLoading ? 1 : 850,
icon: agamaIcon,
show: false,
});
if (hideLoadingWindow && if (appConfig.dev) {
loadingWindow) { mainWindow.loadURL('http://127.0.0.1:3000');
loadingWindow.hide(); } else {
} mainWindow.loadURL(`http://${appConfig.host}:${appConfig.agamaPort}/gui/EasyDEX-GUI/react/build`);
}, 40); }
});
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)
shepherd.writeLog('show edex gui');
mainWindow.appConfig = appConfig;
mainWindow.appConfigSchema = shepherd.appConfigSchema;
mainWindow.arch = arch();
mainWindow.appBasicInfo = appBasicInfo;
mainWindow.appSessionHash = appSessionHash;
mainWindow.assetChainPorts = require('./routes/ports.js');
mainWindow.agamaIcon = agamaIcon;
mainWindow.testLocation = shepherd.testLocation;
mainWindow.kmdMainPassiveMode = shepherd.kmdMainPassiveMode;
mainWindow.getAppRuntimeLog = shepherd.getAppRuntimeLog;
mainWindow.nativeCoindList = nativeCoindList;
mainWindow.zcashParamsExist = _zcashParamsExist;
mainWindow.zcashParamsExistPromise = shepherd.zcashParamsExistPromise;
mainWindow.zcashParamsDownloadLinks = shepherd.zcashParamsDownloadLinks;
mainWindow.isWindows = os.platform() === 'win32' ? true : false; // obsolete(?)
mainWindow.appExit = appExit;
mainWindow.getMaxconKMDConf = shepherd.getMaxconKMDConf;
mainWindow.setMaxconKMDConf = shepherd.setMaxconKMDConf;
mainWindow.getMMCacheData = shepherd.getMMCacheData;
mainWindow.activeSection = 'wallets';
mainWindow.argv = process.argv;
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,
};
for (let i = 0; i < process.argv.length; i++) {
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 {
mainWindow = new BrowserWindow({
width: 500,
height: 355,
frame: false,
icon: agamaIcon,
show: false,
});
mainWindow.webContents.on('context-menu', (e, params) => { // context-menu returns params mainWindow.setResizable(false);
const { selectionText, isEditable } = params; // params obj mainWindow.forseCloseApp = forseCloseApp;
if (isEditable) { willQuitApp = true;
editMenu.popup(mainWindow); server.listen(appConfig.agamaPort + 1, () => {
} else if (selectionText && selectionText.trim() !== '') { shepherd.log(`guiapp and sockets.io are listening on port ${appConfig.agamaPort + 1}`);
staticMenu.popup(mainWindow); 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');
} }
});
// DEVTOOLS - only for dev purposes - ca333
// mainWindow.webContents.openDevTools()
function appExit() {
const CloseDaemons = () => {
return new Promise((resolve, reject) => {
shepherd.log('Closing Main Window...');
shepherd.writeLog('exiting app...');
shepherd.quitKomodod(appConfig.cliStopTimeout); mainWindow.webContents.on('did-finish-load', () => {
setTimeout(() => {
mainWindow.show();
}, 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
const { selectionText, isEditable } = params; // params obj
if (isEditable) {
editMenu.popup(mainWindow);
} else if (selectionText && selectionText.trim() !== '') {
staticMenu.popup(mainWindow);
}
});
const result = 'Closing daemons: done'; // DEVTOOLS - only for dev purposes - ca333
// mainWindow.webContents.openDevTools()
shepherd.log(result); function appExit() {
shepherd.writeLog(result); const CloseDaemons = () => {
resolve(result); return new Promise((resolve, reject) => {
}); shepherd.log('Closing Main Window...');
} shepherd.writeLog('exiting app...');
const HideMainWindow = () => { shepherd.quitKomodod(appConfig.cliStopTimeout);
return new Promise((resolve, reject) => {
const result = 'Hiding Main Window: done';
shepherd.log('Exiting App...'); const result = 'Closing daemons: done';
mainWindow = null;
shepherd.log(result);
resolve(result);
});
}
const HideAppClosingWindow = () => { shepherd.log(result);
return new Promise((resolve, reject) => { shepherd.writeLog(result);
appCloseWindow = null; resolve(result);
resolve(true); });
}); }
}
const QuitApp = () => { const HideMainWindow = () => {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const result = 'Quiting App: done'; const result = 'Hiding Main Window: done';
app.quit(); shepherd.log('Exiting App...');
shepherd.log(result); mainWindow = null;
resolve(result); shepherd.log(result);
}); resolve(result);
} });
}
const closeApp = () => { const HideAppClosingWindow = () => {
CloseDaemons() return new Promise((resolve, reject) => {
.then(HideMainWindow) appCloseWindow = null;
.then(HideAppClosingWindow) resolve(true);
.then(QuitApp); });
} }
let _appClosingInterval; const QuitApp = () => {
return new Promise((resolve, reject) => {
const result = 'Quiting App: done';
// shepherd.killRogueProcess('marketmaker'); app.quit();
if (!Object.keys(shepherd.coindInstanceRegistry).length || shepherd.log(result);
!appConfig.stopNativeDaemonsOnQuit) { resolve(result);
closeApp(); });
} else { }
createAppCloseWindow();
shepherd.quitKomodod(appConfig.cliStopTimeout); const closeApp = () => {
_appClosingInterval = setInterval(() => { CloseDaemons()
if (!Object.keys(shepherd.coindInstanceRegistry).length) { .then(HideMainWindow)
closeApp(); .then(HideAppClosingWindow)
} .then(QuitApp);
}, 1000); }
let _appClosingInterval;
if (process.argv.indexOf('dexonly') > -1) {
shepherd.killRogueProcess('marketmaker');
}
if (!Object.keys(shepherd.coindInstanceRegistry).length ||
!appConfig.stopNativeDaemonsOnQuit) {
closeApp();
} else {
createAppCloseWindow();
shepherd.quitKomodod(appConfig.cliStopTimeout);
_appClosingInterval = setInterval(() => {
if (!Object.keys(shepherd.coindInstanceRegistry).length) {
closeApp();
}
}, 1000);
}
} }
}
// if window closed we kill iguana proc // close app
mainWindow.on('closed', () => { mainWindow.on('closed', () => {
appExit(); appExit();
});
}); });
} }
} }
app.on('window-all-closed', () => { app.on('window-all-closed', () => {
//if (os.platform() !== 'win32') { ig.kill(); } // if (os.platform() !== 'win32') { ig.kill(); }
// in osx apps stay active in menu bar until explictly closed or quitted by CMD Q // in osx apps stay active in menu bar until explictly closed or quitted by CMD Q
// so we do not kill the app --> for the case user clicks again on the iguana icon // so we do not kill the app --> for the case user clicks again on the iguana icon
// we open just a new window and respawn iguana proc // we open just a new window and respawn iguana proc
/*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;

461
routes/electrumjs/electrumjs.networks.js

@ -2,204 +2,363 @@
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 = {
messagePrefix: '\x19Litecoin Signed Message:\n', messagePrefix: '\x19Litecoin Signed Message:\n',
bip32: { bip32: {
public: 0x019da462, public: 0x019da462,
private: 0x019d9cfe private: 0x019d9cfe
}, },
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',
bip32: { bip32: {
public: 0x02facafd, public: 0x02facafd,
private: 0x02fac398, private: 0x02fac398,
}, },
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
networks.monacoin = { networks.monacoin = {
messagePrefix: '\x19Monacoin Signed Message:\n', messagePrefix: '\x19Monacoin Signed Message:\n',
bip32: { bip32: {
public: 0x0488b21e, public: 0x0488b21e,
private: 0x0488ade4, private: 0x0488ade4,
}, },
pubKeyHash: 0x32, pubKeyHash: 0x32,
scriptHash: 0x05, scriptHash: 0x05,
wif: 0xB2, wif: 0xB2,
dustThreshold: 546, // https://github.com/bitcoin/bitcoin/blob/v0.9.2/src/core.h#L151-L162 dustThreshold: 546, // https://github.com/bitcoin/bitcoin/blob/v0.9.2/src/core.h#L151-L162
}; };
// https://github.com/gamecredits-project/GameCredits/blob/master/src/chainparams.cpp#L136 // https://github.com/gamecredits-project/GameCredits/blob/master/src/chainparams.cpp#L136
networks.game = { networks.game = {
messagePrefix: '\x19GameCredits Signed Message:\n', messagePrefix: '\x19GameCredits Signed Message:\n',
bip32: { bip32: {
public: 0x043587cf, public: 0x043587cf,
private: 0x04358394, private: 0x04358394,
}, },
pubKeyHash: 0x6f, pubKeyHash: 0x6f,
scriptHash: 0xc4, scriptHash: 0xc4,
wif: 0xef, wif: 0xef,
dustThreshold: 546, // https://github.com/bitcoin/bitcoin/blob/v0.9.2/src/core.h#L151-L162 dustThreshold: 546, // https://github.com/bitcoin/bitcoin/blob/v0.9.2/src/core.h#L151-L162
}; };
// https://github.com/dashpay/dash/blob/master/src/chainparams.cpp#L171 // https://github.com/dashpay/dash/blob/master/src/chainparams.cpp#L171
networks.dash = { networks.dash = {
messagePrefix: '\x19DarkCoin Signed Message:\n', messagePrefix: '\x19DarkCoin Signed Message:\n',
bip32: { bip32: {
public: 0x02fe52f8, public: 0x02fe52f8,
private: 0x02fe52cc, private: 0x02fe52cc,
}, },
pubKeyHash: 0x4c, pubKeyHash: 0x4c,
scriptHash: 0x10, scriptHash: 0x10,
wif: 0xcc, wif: 0xcc,
dustThreshold: 5460, // https://github.com/dashpay/dash/blob/v0.12.0.x/src/primitives/transaction.h#L144-L155 dustThreshold: 5460, // https://github.com/dashpay/dash/blob/v0.12.0.x/src/primitives/transaction.h#L144-L155
}; };
// https://github.com/zcoinofficial/zcoin/blob/c93eccb39b07a6132cb3d787ac18be406b24c3fa/src/base58.h#L275 // https://github.com/zcoinofficial/zcoin/blob/c93eccb39b07a6132cb3d787ac18be406b24c3fa/src/base58.h#L275
networks.zcoin = { networks.zcoin = {
messagePrefix: '\x19ZCoin Signed Message:\n', messagePrefix: '\x19ZCoin Signed Message:\n',
bip32: { bip32: {
public: 0x0488b21e, // todo public: 0x0488b21e, // todo
private: 0x0488ade4, // todo private: 0x0488ade4, // todo
}, },
pubKeyHash: 0x52, pubKeyHash: 0x52,
scriptHash: 0x07, scriptHash: 0x07,
wif: 0x52 + 128, wif: 0x52 + 128,
dustThreshold: 1000, // https://github.com/zcoinofficial/zcoin/blob/f755f95a036eedfef7c96bcfb6769cb79278939f/src/main.h#L59 dustThreshold: 1000, // https://github.com/zcoinofficial/zcoin/blob/f755f95a036eedfef7c96bcfb6769cb79278939f/src/main.h#L59
}; };
// https://raw.githubusercontent.com/jl777/komodo/beta/src/chainparams.cpp // https://raw.githubusercontent.com/jl777/komodo/beta/src/chainparams.cpp
networks.komodo = { networks.komodo = {
messagePrefix: '\x19Komodo Signed Message:\n', messagePrefix: '\x19Komodo Signed Message:\n',
bip32: { bip32: {
public: 0x0488b21e, public: 0x0488b21e,
private: 0x0488ade4, private: 0x0488ade4,
}, },
pubKeyHash: 0x3c, pubKeyHash: 0x3c,
scriptHash: 0x55, scriptHash: 0x55,
wif: 0xbc, wif: 0xbc,
dustThreshold: 1000, dustThreshold: 1000,
}; };
networks.viacoin = { networks.viacoin = {
messagePrefix: '\x19Viacoin Signed Message:\n', messagePrefix: '\x19Viacoin Signed Message:\n',
bip32: { bip32: {
public: 0x0488b21e, public: 0x0488b21e,
private: 0x0488ade4, private: 0x0488ade4,
}, },
pubKeyHash: 0x47, pubKeyHash: 0x47,
scriptHash: 0x21, scriptHash: 0x21,
wif: 0xc7, wif: 0xc7,
dustThreshold: 1000, dustThreshold: 1000,
}; };
networks.vertcoin = { networks.vertcoin = {
messagePrefix: '\x19Vertcoin Signed Message:\n', messagePrefix: '\x19Vertcoin Signed Message:\n',
bip32: { bip32: {
public: 0x0488b21e, public: 0x0488b21e,
private: 0x0488ade4, private: 0x0488ade4,
}, },
pubKeyHash: 0x47, pubKeyHash: 0x47,
scriptHash: 0x5, scriptHash: 0x5,
wif: 0x80, wif: 0x80,
dustThreshold: 1000, dustThreshold: 1000,
}; };
networks.namecoin = { networks.namecoin = {
messagePrefix: '\x19Namecoin Signed Message:\n', messagePrefix: '\x19Namecoin Signed Message:\n',
bip32: { bip32: {
public: 0x0488b21e, public: 0x0488b21e,
private: 0x0488ade4, private: 0x0488ade4,
}, },
pubKeyHash: 0x34, pubKeyHash: 0x34,
scriptHash: 0xd, scriptHash: 0xd,
wif: 0xb4, wif: 0xb4,
dustThreshold: 1000, dustThreshold: 1000,
}; };
networks.faircoin = { networks.faircoin = {
messagePrefix: '\x19Faircoin Signed Message:\n', messagePrefix: '\x19Faircoin Signed Message:\n',
bip32: { bip32: {
public: 0x0488b21e, public: 0x0488b21e,
private: 0x0488ade4, private: 0x0488ade4,
}, },
pubKeyHash: 0x5f, pubKeyHash: 0x5f,
scriptHash: 0x24, scriptHash: 0x24,
wif: 0xdf, wif: 0xdf,
dustThreshold: 1000, dustThreshold: 1000,
}; };
networks.digibyte = { networks.digibyte = {
messagePrefix: '\x19Digibyte Signed Message:\n', messagePrefix: '\x19Digibyte Signed Message:\n',
bip32: { bip32: {
public: 0x0488b21e, public: 0x0488b21e,
private: 0x0488ade4, private: 0x0488ade4,
}, },
pubKeyHash: 0x1e, pubKeyHash: 0x1e,
scriptHash: 0x5, scriptHash: 0x5,
wif: 0x80, wif: 0x80,
dustThreshold: 1000, dustThreshold: 1000,
}; };
networks.crown = { networks.crown = {
messagePrefix: '\x19Crown Signed Message:\n', messagePrefix: '\x19Crown Signed Message:\n',
bip32: { bip32: {
public: 0x0488b21e, public: 0x0488b21e,
private: 0x0488ade4, private: 0x0488ade4,
}, },
pubKeyHash: 0x0, pubKeyHash: 0x0,
scriptHash: 0x1c, scriptHash: 0x1c,
wif: 0x80, wif: 0x80,
dustThreshold: 1000, dustThreshold: 1000,
}; };
networks.argentum = { networks.argentum = {
messagePrefix: '\x19Argentum Signed Message:\n', messagePrefix: '\x19Argentum Signed Message:\n',
bip32: { bip32: {
public: 0x0488b21e, public: 0x0488b21e,
private: 0x0488ade4, private: 0x0488ade4,
}, },
pubKeyHash: 0x17, pubKeyHash: 0x17,
scriptHash: 0x5, scriptHash: 0x5,
wif: 0x97, wif: 0x97,
dustThreshold: 1000, dustThreshold: 1000,
}; };
networks.chips = { networks.chips = {
messagePrefix: '\x19Chips Signed Message:\n', messagePrefix: '\x19Chips Signed Message:\n',
bip32: { bip32: {
public: 0x0488b21e, public: 0x0488b21e,
private: 0x0488ade4, private: 0x0488ade4,
}, },
pubKeyHash: 0x3c, pubKeyHash: 0x3c,
scriptHash: 0x55, scriptHash: 0x55,
wif: 0xbc, wif: 0xbc,
dustThreshold: 1000, dustThreshold: 1000,
}; };
/*networks.zcash = { networks.btg = {
messagePrefix: '\x19Zcash Signed Message:\n', messagePrefix: '\x19BitcoinGold Signed Message:\n',
bip32: { bip32: {
public: 0x0488b21e, public: 0x0488b21e,
private: 0x0488ade4, private: 0x0488ade4,
}, },
pubKeyHash: 0x1cb8, pubKeyHash: 0x26,
scriptHash: 0x1cbd, scriptHash: 0x17,
wif: 0x80, wif: 0x80,
dustThreshold: 1000, 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',
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: {
public: 0x0488b21e,
private: 0x0488ade4,
},
pubKeyHash: 0x1cb8,
scriptHash: 0x1cbd,
wif: 0x80,
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);

152
routes/shepherd/addCoinShortcuts.js

@ -1,3 +1,5 @@
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') {
@ -5,7 +7,13 @@ module.exports = (shepherd) => {
shepherd.addElectrumCoin('REVS'); shepherd.addElectrumCoin('REVS');
shepherd.addElectrumCoin('JUMBLR'); shepherd.addElectrumCoin('JUMBLR');
} else { } else {
shepherd.addElectrumCoin(coin); if (process.argv.indexOf('spvcoins=all/add-all') > -1) {
for (let key in electrumServers) {
shepherd.addElectrumCoin(electrumServers[key].abbr);
}
} else {
shepherd.addElectrumCoin(coin);
}
} }
} }
@ -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) => {

27
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) => {
const obj = shepherd.SystemInfo(); if (shepherd.checkToken(req.query.token)) {
res.send(obj); const obj = shepherd.SystemInfo();
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) => {
const obj = shepherd.appInfo(); if (shepherd.checkToken(req.query.token)) {
res.send(obj); const obj = shepherd.appInfo();
res.send(obj);
} else {
const errorObj = {
msg: 'error',
result: 'unauthorized access',
};
res.end(JSON.stringify(errorObj));
}
}); });
return shepherd; return shepherd;

48
routes/shepherd/auth.js

@ -3,29 +3,45 @@ module.exports = (shepherd) => {
* type: GET * type: GET
* *
*/ */
shepherd.get('/auth/status', (req, res, next) => { // not finished shepherd.get('/auth/status', (req, res, next) => {
let successObj; if (shepherd.checkToken(req.query.token)) {
let _status = false; let successObj;
let _status = false;
if (Object.keys(shepherd.coindInstanceRegistry).length) { if (Object.keys(shepherd.coindInstanceRegistry).length) {
if (Object.keys(shepherd.electrumCoins).length > 1 && if (Object.keys(shepherd.electrumCoins).length > 1 &&
shepherd.electrumCoins.auth) { shepherd.electrumCoins.auth) {
_status = true;
} else if (Object.keys(shepherd.electrumCoins).length === 1 && !shepherd.electrumCoins.auth) {
_status = true;
}
} else if (Object.keys(shepherd.electrumCoins).length > 1 && shepherd.electrumCoins.auth) {
_status = true; _status = true;
} else if (Object.keys(shepherd.electrumCoins).length === 1 && !shepherd.electrumCoins.auth) { } else if (Object.keys(shepherd.electrumCoins).length === 1 && !Object.keys(shepherd.coindInstanceRegistry).length) {
_status = true; _status = true;
} }
} else if (Object.keys(shepherd.electrumCoins).length > 1 && shepherd.electrumCoins.auth) {
_status = true;
} else if (Object.keys(shepherd.electrumCoins).length === 1 && !Object.keys(shepherd.coindInstanceRegistry).length) {
_status = true;
}
successObj = { successObj = {
status: _status ? 'unlocked' : 'locked', status: _status ? 'unlocked' : 'locked',
}; };
res.end(JSON.stringify(successObj));
} else {
const errorObj = {
msg: 'error',
result: 'unauthorized access',
};
res.end(JSON.stringify(successObj)); 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;
};

141
routes/shepherd/coindWalletKeys.js

@ -4,94 +4,103 @@ module.exports = (shepherd) => {
* *
*/ */
shepherd.get('/coindwalletkeys', (req, res, next) => { shepherd.get('/coindwalletkeys', (req, res, next) => {
const wif = require('wif'); if (shepherd.checkToken(req.query.token)) {
const fs = require('fs'); const wif = require('wif');
const chain = req.query.chain; const fs = require('fs');
const chain = req.query.chain;
// ref: https://gist.github.com/kendricktan/1e62495150ad236b38616d733aac4eb9 // ref: https://gist.github.com/kendricktan/1e62495150ad236b38616d733aac4eb9
let _walletDatLocation = chain === 'komodo' || chain === 'null' ? `${shepherd.komodoDir}/wallet.dat` : `${shepherd.komodoDir}/${chain}/wallet.dat`; let _walletDatLocation = chain === 'komodo' || chain === 'null' ? `${shepherd.komodoDir}/wallet.dat` : `${shepherd.komodoDir}/${chain}/wallet.dat`;
_walletDatLocation = chain === 'CHIPS' ? `${shepherd.chipsDir}/wallet.dat` : _walletDatLocation; _walletDatLocation = chain === 'CHIPS' ? `${shepherd.chipsDir}/wallet.dat` : _walletDatLocation;
try { try {
shepherd._fs.access(_walletDatLocation, shepherd.fs.constants.R_OK, (err) => { shepherd._fs.access(_walletDatLocation, shepherd.fs.constants.R_OK, (err) => {
if (err) { if (err) {
shepherd.log(`error reading ${_walletDatLocation}`); shepherd.log(`error reading ${_walletDatLocation}`);
successObj = { successObj = {
msg: 'error', msg: 'error',
result: `error reading ${_walletDatLocation}`, result: `error reading ${_walletDatLocation}`,
}; };
res.end(JSON.stringify(successObj));
} else {
shepherd.log(`reading ${_walletDatLocation}`);
fs.readFile(_walletDatLocation, (err, data) => {
if (err) {
shepherd.log(`read wallet.dat err: ${err}`);
successObj = {
msg: 'error',
result: `error reading ${_walletDatLocation}`,
};
res.end(JSON.stringify(successObj));
} else {
const re = /\x30\x81\xD3\x02\x01\x01\x04\x20(.{32})/gm;
const dataHexStr = data.toString('latin1');
privateKeys = dataHexStr.match(re);
if (!privateKeys) {
shepherd.log('wallet is encrypted?');
res.end(JSON.stringify(successObj));
} else {
shepherd.log(`reading ${_walletDatLocation}`);
fs.readFile(_walletDatLocation, (err, data) => {
if (err) {
shepherd.log(`read wallet.dat err: ${err}`);
successObj = { successObj = {
msg: 'error', msg: 'error',
result: 'wallet is encrypted?', result: `error reading ${_walletDatLocation}`,
}; };
res.end(JSON.stringify(successObj)); res.end(JSON.stringify(successObj));
} else { } else {
let _keys = []; const re = /\x30\x81\xD3\x02\x01\x01\x04\x20(.{32})/gm;
privateKeys = privateKeys.map(x => x.replace('\x30\x81\xD3\x02\x01\x01\x04\x20', '')); const dataHexStr = data.toString('latin1');
privateKeys = privateKeys.filter((v, i, a) => a.indexOf(v) === i); privateKeys = dataHexStr.match(re);
shepherd.log(`found ${privateKeys.length} keys`);
for (let i = 0; i < privateKeys.length; i++) { if (!privateKeys) {
const privateKey = new Buffer(Buffer.from(privateKeys[i], 'latin1').toString('hex'), 'hex'); shepherd.log('wallet is encrypted?');
const key = wif.encode(0xbc, privateKey, true);
const keyObj = wif.decode(key);
const wifKey = wif.encode(keyObj);
const keyPair = shepherd.bitcoinJS.ECPair.fromWIF(wifKey, shepherd.electrumJSNetworks.komodo); successObj = {
const _keyPair = { msg: 'error',
priv: keyPair.toWIF(), result: 'wallet is encrypted?',
pub: keyPair.getAddress(),
}; };
if (req.query.search) { res.end(JSON.stringify(successObj));
if (_keyPair.pub.indexOf(req.query.search) > -1) { } else {
let _keys = [];
privateKeys = privateKeys.map(x => x.replace('\x30\x81\xD3\x02\x01\x01\x04\x20', ''));
privateKeys = privateKeys.filter((v, i, a) => a.indexOf(v) === i);
shepherd.log(`found ${privateKeys.length} keys`);
for (let i = 0; i < privateKeys.length; i++) {
const privateKey = new Buffer(Buffer.from(privateKeys[i], 'latin1').toString('hex'), 'hex');
const key = wif.encode(0xbc, privateKey, true);
const keyObj = wif.decode(key);
const wifKey = wif.encode(keyObj);
const keyPair = shepherd.bitcoinJS.ECPair.fromWIF(wifKey, shepherd.electrumJSNetworks.komodo);
const _keyPair = {
priv: keyPair.toWIF(),
pub: keyPair.getAddress(),
};
if (req.query.search) {
if (_keyPair.pub.indexOf(req.query.search) > -1) {
_keys.push(_keyPair);
}
} else {
_keys.push(_keyPair); _keys.push(_keyPair);
} }
} else {
_keys.push(_keyPair);
} }
}
successObj = { successObj = {
msg: 'success', msg: 'success',
result: _keys, result: _keys,
}; };
res.end(JSON.stringify(successObj)); res.end(JSON.stringify(successObj));
}
} }
} });
}); }
} });
}); } catch (e) {
} catch (e) { successObj = {
successObj = { msg: 'error',
result: `error reading ${_walletDatLocation}`,
};
res.end(JSON.stringify(successObj));
}
} else {
const errorObj = {
msg: 'error', msg: 'error',
result: `error reading ${_walletDatLocation}`, result: 'unauthorized access',
}; };
res.end(JSON.stringify(successObj)); res.end(JSON.stringify(errorObj));
} }
}); });

41
routes/shepherd/coins.js

@ -4,27 +4,36 @@ module.exports = (shepherd) => {
* *
*/ */
shepherd.get('/InstantDEX/allcoins', (req, res, next) => { shepherd.get('/InstantDEX/allcoins', (req, res, next) => {
let successObj; if (shepherd.checkToken(req.query.token)) {
let nativeCoindList = []; let successObj;
let electrumCoinsList = []; let nativeCoindList = [];
let electrumCoinsList = [];
for (let key in shepherd.electrumCoins) { for (let key in shepherd.electrumCoins) {
if (key !== 'auth') { if (key !== 'auth') {
electrumCoinsList.push(shepherd.electrumCoins[key].abbr); electrumCoinsList.push(shepherd.electrumCoins[key].abbr);
}
} }
}
for (let key in shepherd.coindInstanceRegistry) { for (let key in shepherd.coindInstanceRegistry) {
nativeCoindList.push(key === 'komodod' ? 'KMD' : key); nativeCoindList.push(key === 'komodod' ? 'KMD' : key);
} }
successObj = { successObj = {
native: nativeCoindList, native: nativeCoindList,
spv: electrumCoinsList, spv: electrumCoinsList,
total: Object.keys(shepherd.electrumCoins).length - 1 + Object.keys(nativeCoindList).length, total: Object.keys(shepherd.electrumCoins).length - 1 + Object.keys(nativeCoindList).length,
}; };
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;

94
routes/shepherd/coinsList.js

@ -4,28 +4,37 @@ module.exports = (shepherd) => {
* *
*/ */
shepherd.get('/coinslist', (req, res, next) => { shepherd.get('/coinslist', (req, res, next) => {
if (shepherd.fs.existsSync(`${shepherd.agamaDir}/shepherd/coinslist.json`)) { if (shepherd.checkToken(req.query.token)) {
shepherd.fs.readFile(`${shepherd.agamaDir}/shepherd/coinslist.json`, 'utf8', (err, data) => { if (shepherd.fs.existsSync(`${shepherd.agamaDir}/shepherd/coinslist.json`)) {
if (err) { shepherd.fs.readFile(`${shepherd.agamaDir}/shepherd/coinslist.json`, 'utf8', (err, data) => {
const errorObj = { if (err) {
msg: 'error', const errorObj = {
result: err, msg: 'error',
}; result: err,
};
res.end(JSON.stringify(errorObj)); res.end(JSON.stringify(errorObj));
} else { } else {
const successObj = { const successObj = {
msg: 'success', msg: 'success',
result: data ? JSON.parse(data) : '', result: data ? JSON.parse(data) : '',
}; };
res.end(JSON.stringify(successObj)); res.end(JSON.stringify(successObj));
} }
}); });
} else {
const errorObj = {
msg: 'error',
result: 'coin list doesn\'t exist',
};
res.end(JSON.stringify(errorObj));
}
} else { } else {
const errorObj = { const errorObj = {
msg: 'error', msg: 'error',
result: 'coin list doesn\'t exist', result: 'unauthorized access',
}; };
res.end(JSON.stringify(errorObj)); res.end(JSON.stringify(errorObj));
@ -37,33 +46,42 @@ module.exports = (shepherd) => {
* params: payload * params: payload
*/ */
shepherd.post('/coinslist', (req, res, next) => { shepherd.post('/coinslist', (req, res, next) => {
const _payload = req.body.payload; if (shepherd.checkToken(req.body.token)) {
const _payload = req.body.payload;
if (!_payload) {
const errorObj = {
msg: 'error',
result: 'no payload provided',
};
res.end(JSON.stringify(errorObj));
} else {
shepherd.fs.writeFile(`${shepherd.agamaDir}/shepherd/coinslist.json`, JSON.stringify(_payload), (err) => {
if (err) {
const errorObj = {
msg: 'error',
result: err,
};
res.end(JSON.stringify(errorObj));
} else {
const successObj = {
msg: 'success',
result: 'done',
};
if (!_payload) { res.end(JSON.stringify(successObj));
}
});
}
} else {
const errorObj = { const errorObj = {
msg: 'error', msg: 'error',
result: 'no payload provided', result: 'unauthorized access',
}; };
res.end(JSON.stringify(errorObj)); res.end(JSON.stringify(errorObj));
} else {
shepherd.fs.writeFile(`${shepherd.agamaDir}/shepherd/coinslist.json`, JSON.stringify(_payload), (err) => {
if (err) {
const errorObj = {
msg: 'error',
result: err,
};
res.end(JSON.stringify(errorObj));
} else {
const successObj = {
msg: 'success',
result: 'done',
};
res.end(JSON.stringify(successObj));
}
});
} }
}); });

65
routes/shepherd/config.js

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

280
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) {
nativeCoindDir = coind ? `${process.env.HOME}/Library/Application Support/${shepherd.nativeCoindList[coind.toLowerCase()].bin}` : null; case 'darwin':
} nativeCoindDir = coind ? `${process.env.HOME}/Library/Application Support/${shepherd.nativeCoindList[coind.toLowerCase()].bin}` : null;
break;
if (os.platform() === 'linux') { case '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,26 +657,28 @@ 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');
} else { shepherd.writeLog('rpcport: OK');
shepherd.log('rpcbind: NOT FOUND'); } else {
shepherd.writeLog('rpcbind: NOT FOUND'); shepherd.log('rpcport: 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,66 +792,81 @@ 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 =>');
if (req.body.options && shepherd.log(_body);
!shepherd.kmdMainPassiveMode) {
const testCoindPort = (skipError) => { if (_body.options &&
if (!shepherd.lockDownAddCoin) { !shepherd.kmdMainPassiveMode) {
const _port = shepherd.assetChainPorts[req.body.options.ac_name]; const testCoindPort = (skipError) => {
const _acName = req.body.options.ac_name;
portscanner.checkPortStatus(_port, '127.0.0.1', (error, status) => {
// Status is 'open' if currently in use or 'closed' if available if (!shepherd.lockDownAddCoin) {
if (status === 'open' && const _port = shepherd.assetChainPorts[_acName];
shepherd.appConfig.stopNativeDaemonsOnQuit) {
if (!skipError) { portscanner.checkPortStatus(_port, '127.0.0.1', (error, status) => {
shepherd.log(`komodod service start error at port ${_port}, reason: port is closed`); // Status is 'open' if currently in use or 'closed' if available
shepherd.writeLog(`komodod service start error at port ${_port}, reason: port is closed`); if (status === 'open' &&
shepherd.io.emit('service', { shepherd.appConfig.stopNativeDaemonsOnQuit) {
komodod: { if (!skipError) {
error: `error starting ${req.body.herd} ${req.body.options.ac_name} daemon. Port ${_port} is already taken!`, shepherd.log(`komodod service start error at port ${_port}, reason: port is closed`);
}, shepherd.writeLog(`komodod service start error at port ${_port}, reason: port is closed`);
}); shepherd.io.emit('service', {
komodod: {
error: `error starting ${_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);
res.end(JSON.stringify(obj)); res.end(JSON.stringify(obj));
} else {
shepherd.log(`komodod service start success at port ${_port}`);
shepherd.writeLog(`komodod service start success at port ${_port}`);
}
} else { } else {
shepherd.log(`komodod service start success at port ${_port}`); if (!skipError) {
shepherd.writeLog(`komodod service start success at port ${_port}`); herder(_body.herd, _body.options);
}
} else {
if (!skipError) {
herder(req.body.herd, req.body.options);
const obj = { const obj = {
msg: 'success', msg: 'success',
result: 'result', result: 'result',
}; };
res.end(JSON.stringify(obj)); res.end(JSON.stringify(obj));
} else { } else {
shepherd.log(`komodod service start error at port ${_port}, reason: unknown`); shepherd.log(`komodod service start error at port ${_port}, reason: unknown`);
shepherd.writeLog(`komodod service start error at port ${_port}, reason: unknown`); shepherd.writeLog(`komodod service start error at port ${_port}, reason: unknown`);
}
} }
} });
}); }
} }
}
if (req.body.herd === 'komodod') { 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 {
herder(_body.herd, _body.options, _body.coind);
const obj = {
msg: 'success',
result: 'result',
};
res.end(JSON.stringify(obj));
}
} else { } else {
herder(req.body.herd, req.body.options, req.body.coind); // (?)
herder(_body.herd, _body.options);
const obj = { const obj = {
msg: 'success', msg: 'success',
@ -857,15 +876,12 @@ module.exports = (shepherd) => {
res.end(JSON.stringify(obj)); res.end(JSON.stringify(obj));
} }
} else { } else {
// (?) const errorObj = {
herder(req.body.herd, req.body.options); msg: 'error',
result: 'unauthorized access',
const obj = {
msg: 'success',
result: 'result',
}; };
res.end(JSON.stringify(obj)); res.end(JSON.stringify(errorObj));
} }
}); });
@ -873,44 +889,64 @@ module.exports = (shepherd) => {
* 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;
if (os.platform() === 'win32' && shepherd.log('setconf req.body =>');
req.body.chain == 'komodod') { shepherd.log(_body);
setkomodoconf = spawn(path.join(__dirname, '../assets/bin/win64/genkmdconf.bat'));
} else { if (os.platform() === 'win32' &&
shepherd.setConf(req.body.chain); _body.chain == 'komodod') {
} setkomodoconf = spawn(path.join(__dirname, '../assets/bin/win64/genkmdconf.bat'));
} else {
shepherd.setConf(_body.chain);
}
const obj = {
msg: 'success',
result: 'result',
};
const obj = { res.end(JSON.stringify(obj));
msg: 'success', } else {
result: 'result', const errorObj = {
}; msg: 'error',
result: 'unauthorized access',
};
res.end(JSON.stringify(obj)); 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;
const confpath = getConf(req.body.chain, req.body.coind); shepherd.log('getconf req.body =>');
shepherd.log(_body);
shepherd.log('got conf path is:'); const confpath = getConf(_body.chain, _body.coind);
shepherd.log(confpath);
shepherd.writeLog('got conf path is:');
shepherd.writeLog(confpath);
const obj = { shepherd.log(`getconf path is: ${confpath}`);
msg: 'success', shepherd.writeLog(`getconf path is: ${confpath}`);
result: confpath,
};
res.end(JSON.stringify(obj)); const obj = {
msg: 'success',
result: confpath,
};
res.end(JSON.stringify(obj));
} else {
const errorObj = {
msg: 'error',
result: 'unauthorized access',
};
res.end(JSON.stringify(errorObj));
}
}); });
shepherd.setConfKMD = (isChips) => { shepherd.setConfKMD = (isChips) => {

518
routes/shepherd/dashboardUpdate.js

@ -7,171 +7,189 @@ module.exports = (shepherd) => {
* params: coin * params: coin
*/ */
shepherd.post('/native/dashboard/update', (req, res, next) => { shepherd.post('/native/dashboard/update', (req, res, next) => {
const _coin = req.body.coin; if (shepherd.checkToken(req.body.token)) {
let _returnObj; const _coin = req.body.coin;
let _promiseStack; const _token = req.body.token;
let _returnObj;
if (_coin === 'CHIPS') { let _promiseStack;
_returnObj = {
getinfo: {}, if (_coin === 'CHIPS') {
listtransactions: [], _returnObj = {
getbalance: {}, getinfo: {},
listunspent: {}, listtransactions: [],
addresses: {}, getbalance: {},
}; listunspent: {},
_promiseStack = [ addresses: {},
'getinfo', };
'listtransactions', _promiseStack = [
'getbalance', 'getinfo',
]; 'listtransactions',
} else { 'getbalance',
_returnObj = { ];
getinfo: {}, } else {
listtransactions: [], _returnObj = {
z_gettotalbalance: {}, getinfo: {},
z_getoperationstatus: {}, listtransactions: [],
listunspent: {}, z_gettotalbalance: {},
addresses: {}, z_getoperationstatus: {},
}; listunspent: {},
_promiseStack = [ addresses: {},
'getinfo', };
'listtransactions', _promiseStack = [
'z_gettotalbalance', 'getinfo',
'z_getoperationstatus' 'listtransactions',
]; 'z_gettotalbalance',
} 'z_getoperationstatus'
];
}
const getAddressesNative = (coin) => { const getAddressesNative = (coin) => {
const type = [ const type = [
'public', 'public',
'private' 'private'
]; ];
if (coin === 'CHIPS') { if (coin === 'CHIPS') {
type.pop(); type.pop();
} }
Promise.all(type.map((_type, index) => { Promise.all(type.map((_type, index) => {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
_bitcoinRPC( _bitcoinRPC(
coin, coin,
_type === 'public' ? 'getaddressesbyaccount' : 'z_listaddresses', _type === 'public' ? 'getaddressesbyaccount' : 'z_listaddresses',
[''] ['']
).then((_json) => { )
if (_json === 'Work queue depth exceeded' || .then((_json) => {
!_json) { if (_json === 'Work queue depth exceeded' ||
resolve({ error: 'daemon is busy' }); !_json) {
} else { resolve({ error: 'daemon is busy' });
resolve(JSON.parse(_json).result); } else {
} resolve(JSON.parse(_json).result);
}
});
}); });
}); }))
})) .then(result => {
.then(result => { if (result[0] &&
if (result[0] && result[0].length &&
result[0].length && result[0][0].length &&
result[0][0].length && result[0][0].length > 10) {
result[0][0].length > 10) { const calcBalance = (result, json) => {
const calcBalance = (result, json) => { if (json &&
if (json && json.length &&
json.length && json[0] &&
json[0] && json[0].address) {
json[0].address) { const allAddrArray = json.map(res => res.address).filter((x, i, a) => a.indexOf(x) == i);
const allAddrArray = json.map(res => res.address).filter((x, i, a) => a.indexOf(x) == i);
for (let a = 0; a < allAddrArray.length; a++) {
for (let a = 0; a < allAddrArray.length; a++) { const filteredArray = json.filter(res => res.address === allAddrArray[a]).map(res => res.amount);
const filteredArray = json.filter(res => res.address === allAddrArray[a]).map(res => res.amount);
let isNewAddr = true;
let isNewAddr = true; for (let x = 0; x < result.length && isNewAddr; x++) {
for (let x = 0; x < result.length && isNewAddr; x++) { for (let y = 0; y < result[x].length && isNewAddr; y++) {
for (let y = 0; y < result[x].length && isNewAddr; y++) { if (allAddrArray[a] === result[x][y]) {
if (allAddrArray[a] === result[x][y]) { isNewAddr = false;
isNewAddr = false; }
} }
} }
}
if (isNewAddr && if (isNewAddr &&
(allAddrArray[a].substring(0, 2) === 'zc' || (allAddrArray[a].substring(0, 2) === 'zc' ||
allAddrArray[a].substring(0, 2) === 'zt')) { allAddrArray[a].substring(0, 2) === 'zt')) {
result[1][result[1].length] = allAddrArray[a]; result[1][result[1].length] = allAddrArray[a];
} else { } else {
result[0][result[0].length] = allAddrArray[a]; result[0][result[0].length] = allAddrArray[a];
}
} }
} }
}
// remove addr duplicates // remove addr duplicates
if (result[0] && if (result[0] &&
result[0].length) { result[0].length) {
result[0] = result[0].filter((elem, pos) => { result[0] = result[0].filter((elem, pos) => {
return result[0].indexOf(elem) === pos; return result[0].indexOf(elem) === pos;
}); });
} }
if (result[1] &&
result[1].length) { if (result[1] &&
result[1] = result[1].filter((elem, pos) => { result[1].length) {
return result[1].indexOf(elem) === pos; result[1] = result[1].filter((elem, pos) => {
}); return result[1].indexOf(elem) === pos;
} });
}
let newAddressArray = []; let newAddressArray = [];
for (let a = 0; a < result.length; a++) {
newAddressArray[a] = [];
if (result[a]) { for (let a = 0; a < result.length; a++) {
for (let b = 0; b < result[a].length; b++) { newAddressArray[a] = [];
const filteredArraySpends = json.filter(res => res.address === result[a][b]);
const filteredArray = json.filter(res => res.address === result[a][b]).map(res => res.amount);
let sum = 0; if (result[a]) {
let spendableSum = 0; for (let b = 0; b < result[a].length; b++) {
let canspend = true; const filteredArraySpends = json.filter(res => res.address === result[a][b]);
const filteredArray = json.filter(res => res.address === result[a][b]).map(res => res.amount);
for (let i = 0; i < filteredArray.length; i++) { let sum = 0;
sum += filteredArray[i]; let spendableSum = 0;
let canspend = true;
if (filteredArraySpends[i].spendable) { for (let i = 0; i < filteredArray.length; i++) {
spendableSum += filteredArray[i]; sum += filteredArray[i];
} else {
canspend = false; if (filteredArraySpends[i].spendable) {
spendableSum += filteredArray[i];
} else {
canspend = false;
}
} }
}
newAddressArray[a][b] = { newAddressArray[a][b] = {
address: result[a][b], address: result[a][b],
amount: sum, amount: sum,
spendable: spendableSum, spendable: spendableSum,
canspend, canspend,
type: a === 0 ? 'public': 'private', type: a === 0 ? 'public': 'private',
}; };
}
} }
} }
}
// get zaddr balance // get zaddr balance
if (result[1] && if (result[1] &&
result[1].length) { result[1].length) {
Promise.all(result[1].map((_address, index) => { Promise.all(result[1].map((_address, index) => {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
_bitcoinRPC(coin, 'z_getbalance', [_address]) _bitcoinRPC(coin, 'z_getbalance', [_address])
.then((__json) => { .then((__json) => {
__json = JSON.parse(__json); __json = JSON.parse(__json);
if (__json && if (__json &&
__json.error) { __json.error) {
resolve(0); resolve(0);
} else { } else {
resolve(__json.result); resolve(__json.result);
newAddressArray[1][index] = { newAddressArray[1][index] = {
address: _address, address: _address,
amount: __json.result, amount: __json.result,
type: 'private', type: 'private',
}; };
} }
});
}); });
}))
.then(zresult => {
_returnObj.addresses = {
public: newAddressArray[0],
private: newAddressArray[1],
};
const returnObj = {
msg: 'success',
result: _returnObj,
};
res.end(JSON.stringify(returnObj));
}); });
})) } else {
.then(zresult => {
_returnObj.addresses = { _returnObj.addresses = {
public: newAddressArray[0], public: newAddressArray[0],
private: newAddressArray[1], private: newAddressArray[1],
@ -183,129 +201,129 @@ module.exports = (shepherd) => {
}; };
res.end(JSON.stringify(returnObj)); res.end(JSON.stringify(returnObj));
}); }
} else {
_returnObj.addresses = {
public: newAddressArray[0],
private: newAddressArray[1],
};
const returnObj = {
msg: 'success',
result: _returnObj,
};
res.end(JSON.stringify(returnObj));
} }
}
_bitcoinRPC(coin, 'listunspent') _bitcoinRPC(coin, 'listunspent')
.then((__json) => { .then((__json) => {
if (__json === 'Work queue depth exceeded' || if (__json === 'Work queue depth exceeded' ||
!__json) { !__json) {
const returnObj = { const returnObj = {
msg: 'success', msg: 'success',
result: _returnObj, result: _returnObj,
}; };
res.end(JSON.stringify(returnObj)); res.end(JSON.stringify(returnObj));
} else { } else {
_returnObj.listunspent = JSON.parse(__json); _returnObj.listunspent = JSON.parse(__json);
calcBalance( calcBalance(
result, result,
JSON.parse(__json).result JSON.parse(__json).result
); );
} }
}); });
} else { } else {
_returnObj.addresses = { _returnObj.addresses = {
public: {}, public: {},
private: {}, private: {},
}; };
const returnObj = { const returnObj = {
msg: 'success', msg: 'success',
result: _returnObj, result: _returnObj,
}; };
res.end(JSON.stringify(returnObj)); res.end(JSON.stringify(returnObj));
} }
}) })
} }
const _bitcoinRPC = (coin, cmd, params) => { const _bitcoinRPC = (coin, cmd, params) => {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
let _payload; let _payload;
if (params) {
_payload = {
mode: null,
chain: coin,
cmd: cmd,
params: params,
rpc2cli: req.body.rpc2cli,
token: _token,
};
} else {
_payload = {
mode: null,
chain: coin,
cmd: cmd,
rpc2cli: req.body.rpc2cli,
token: _token,
};
}
if (params) { const options = {
_payload = { url: `http://127.0.0.1:${shepherd.appConfig.agamaPort}/shepherd/cli`,
mode: null, method: 'POST',
chain: coin, headers: {
cmd: cmd, 'Content-Type': 'application/json',
params: params, },
}; body: JSON.stringify({ payload: _payload }),
} else { timeout: 120000,
_payload = {
mode: null,
chain: coin,
cmd: cmd,
}; };
}
const options = {
url: `http://127.0.0.1:${shepherd.appConfig.agamaPort}/shepherd/cli`,
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ payload: _payload }),
timeout: 120000,
};
shepherd.request(options, (error, response, body) => { shepherd.request(options, (error, response, body) => {
if (response && if (response &&
response.statusCode && response.statusCode &&
response.statusCode === 200) { response.statusCode === 200) {
resolve(body); resolve(body);
} else { } else {
resolve(body); resolve(body);
} }
});
}); });
}); }
}
Promise.all(_promiseStack.map((_call, index) => { Promise.all(_promiseStack.map((_call, index) => {
let _params; let _params;
if (_call === 'listtransactions') { if (_call === 'listtransactions') {
_params = [ _params = [
'*', '*',
300, 300,
0 0
]; ];
} }
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
_bitcoinRPC( _bitcoinRPC(
_coin, _coin,
_call, _call,
_params _params
) )
.then((json) => { .then((json) => {
if (json === 'Work queue depth exceeded' || if (json === 'Work queue depth exceeded' ||
!json) { !json) {
_returnObj[_call] = { error: 'daemon is busy' }; _returnObj[_call] = { error: 'daemon is busy' };
} else { } else {
_returnObj[_call] = JSON.parse(json); _returnObj[_call] = JSON.parse(json);
} }
resolve(json);
resolve(json);
});
}); });
}))
.then(result => {
getAddressesNative(_coin);
}); });
})) } else {
.then(result => { const errorObj = {
getAddressesNative(_coin); msg: 'error',
}); result: 'unauthorized access',
};
res.end(JSON.stringify(errorObj));
}
}); });
return shepherd; return shepherd;

122
routes/shepherd/debugLog.js

@ -4,74 +4,92 @@ module.exports = (shepherd) => {
* params: herd, lastLines * params: herd, lastLines
*/ */
shepherd.post('/debuglog', (req, res) => { shepherd.post('/debuglog', (req, res) => {
let _herd = req.body.herdname; if (shepherd.checkToken(req.body.token)) {
let _ac = req.body.ac; let _herd = req.body.herdname;
let _lastNLines = req.body.lastLines; let _ac = req.body.ac;
let _location; let _lastNLines = req.body.lastLines;
let _location;
if (shepherd.os.platform() === 'darwin') {
shepherd.komodoDir = shepherd.appConfig.dataDir.length ? shepherd.appConfig.dataDir : `${process.env.HOME}/Library/Application Support/Komodo`; if (shepherd.os.platform() === 'darwin') {
} shepherd.komodoDir = shepherd.appConfig.dataDir.length ? shepherd.appConfig.dataDir : `${process.env.HOME}/Library/Application Support/Komodo`;
}
if (shepherd.os.platform() === 'linux') { if (shepherd.os.platform() === 'linux') {
shepherd.komodoDir = shepherd.appConfig.dataDir.length ? shepherd.appConfig.dataDir : `${process.env.HOME}/.komodo`; shepherd.komodoDir = shepherd.appConfig.dataDir.length ? shepherd.appConfig.dataDir : `${process.env.HOME}/.komodo`;
} }
if (shepherd.os.platform() === 'win32') { if (shepherd.os.platform() === 'win32') {
shepherd.komodoDir = shepherd.appConfig.dataDir.length ? shepherd.appConfig.dataDir : `${process.env.APPDATA}/Komodo`; shepherd.komodoDir = shepherd.appConfig.dataDir.length ? shepherd.appConfig.dataDir : `${process.env.APPDATA}/Komodo`;
shepherd.komodoDir = shepherd.path.normalize(shepherd.komodoDir); shepherd.komodoDir = shepherd.path.normalize(shepherd.komodoDir);
} }
if (_herd === 'komodo') { if (_herd === 'komodo') {
_location = shepherd.komodoDir; _location = shepherd.komodoDir;
} }
if (_ac) { if (_ac) {
_location = `${shepherd.komodoDir}/${_ac}`; _location = `${shepherd.komodoDir}/${_ac}`;
if (_ac === 'CHIPS') { if (_ac === 'CHIPS') {
_location = shepherd.chipsDir; _location = shepherd.chipsDir;
}
} }
}
shepherd.readDebugLog(`${_location}/debug.log`, _lastNLines)
.then((result) => {
const _obj = {
msg: 'success',
result: result,
};
res.end(JSON.stringify(_obj)); shepherd.readDebugLog(`${_location}/debug.log`, _lastNLines)
}, (result) => { .then((result) => {
const _obj = { const _obj = {
msg: 'success',
result: result,
};
res.end(JSON.stringify(_obj));
}, (result) => {
const _obj = {
msg: 'error',
result: result,
};
res.end(JSON.stringify(_obj));
});
} else {
const errorObj = {
msg: 'error', msg: 'error',
result: result, result: 'unauthorized access',
}; };
res.end(JSON.stringify(_obj)); res.end(JSON.stringify(errorObj));
}); }
}); });
shepherd.get('/coind/stdout', (req, res) => { shepherd.get('/coind/stdout', (req, res) => {
const _daemonName = req.query.chain !== 'komodod' ? req.query.chain : 'komodod'; if (shepherd.checkToken(req.query.token)) {
const _daemonLogName = `${shepherd.agamaDir}/${_daemonName}.log`; const _daemonName = req.query.chain !== 'komodod' ? req.query.chain : 'komodod';
const _daemonLogName = `${shepherd.agamaDir}/${_daemonName}.log`;
shepherd.readDebugLog(_daemonLogName, 'all')
.then((result) => { shepherd.readDebugLog(_daemonLogName, 'all')
const _obj = { .then((result) => {
msg: 'success', const _obj = {
result: result, msg: 'success',
}; result: result,
};
res.end(JSON.stringify(_obj));
}, (result) => { res.end(JSON.stringify(_obj));
const _obj = { }, (result) => {
const _obj = {
msg: 'error',
result: result,
};
res.end(JSON.stringify(_obj));
});
} else {
const errorObj = {
msg: 'error', msg: 'error',
result: result, result: 'unauthorized access',
}; };
res.end(JSON.stringify(_obj)); res.end(JSON.stringify(errorObj));
}); }
}); });
shepherd.readDebugLog = (fileLocation, lastNLines) => { shepherd.readDebugLog = (fileLocation, lastNLines) => {

208
routes/shepherd/downloadBins.js

@ -46,51 +46,60 @@ module.exports = (shepherd) => {
*/ */
// TODO: promises // TODO: promises
shepherd.get('/update/bins/check', (req, res, next) => { shepherd.get('/update/bins/check', (req, res, next) => {
const rootLocation = shepherd.path.join(__dirname, '../../'); if (shepherd.checkToken(req.query.token)) {
const successObj = { const rootLocation = shepherd.path.join(__dirname, '../../');
msg: 'success', const successObj = {
result: 'bins', msg: 'success',
}; result: 'bins',
};
res.end(JSON.stringify(successObj));
res.end(JSON.stringify(successObj));
const _os = shepherd.os.platform();
shepherd.log(`checking bins: ${_os}`); const _os = shepherd.os.platform();
shepherd.log(`checking bins: ${_os}`);
shepherd.io.emit('patch', {
patch: { shepherd.io.emit('patch', {
type: 'bins-check', patch: {
status: 'progress', type: 'bins-check',
message: `checking bins: ${_os}`, status: 'progress',
}, message: `checking bins: ${_os}`,
}); },
// get list of bins/dlls that can be updated to the latest
for (let i = 0; i < latestBins[_os].length; i++) {
shepherd.remoteFileSize(remoteBinLocation[_os] + latestBins[_os][i], (err, remoteBinSize) => {
const localBinSize = shepherd.fs.statSync(rootLocation + localBinLocation[_os] + latestBins[_os][i]).size;
shepherd.log('remote url: ' + (remoteBinLocation[_os] + latestBins[_os][i]) + ' (' + remoteBinSize + ')');
shepherd.log('local file: ' + (rootLocation + localBinLocation[_os] + latestBins[_os][i]) + ' (' + localBinSize + ')');
if (remoteBinSize !== localBinSize) {
shepherd.log(`${latestBins[_os][i]} can be updated`);
binsToUpdate.push({
name: latestBins[_os][i],
rSize: remoteBinSize,
lSize: localBinSize,
});
}
if (i === latestBins[_os].length - 1) {
shepherd.io.emit('patch', {
patch: {
type: 'bins-check',
status: 'done',
fileList: binsToUpdate,
},
});
}
}); });
// get list of bins/dlls that can be updated to the latest
for (let i = 0; i < latestBins[_os].length; i++) {
shepherd.remoteFileSize(remoteBinLocation[_os] + latestBins[_os][i], (err, remoteBinSize) => {
const localBinSize = shepherd.fs.statSync(rootLocation + localBinLocation[_os] + latestBins[_os][i]).size;
shepherd.log('remote url: ' + (remoteBinLocation[_os] + latestBins[_os][i]) + ' (' + remoteBinSize + ')');
shepherd.log('local file: ' + (rootLocation + localBinLocation[_os] + latestBins[_os][i]) + ' (' + localBinSize + ')');
if (remoteBinSize !== localBinSize) {
shepherd.log(`${latestBins[_os][i]} can be updated`);
binsToUpdate.push({
name: latestBins[_os][i],
rSize: remoteBinSize,
lSize: localBinSize,
});
}
if (i === latestBins[_os].length - 1) {
shepherd.io.emit('patch', {
patch: {
type: 'bins-check',
status: 'done',
fileList: binsToUpdate,
},
});
}
});
}
} else {
const errorObj = {
msg: 'error',
result: 'unauthorized access',
};
res.end(JSON.stringify(errorObj));
} }
}); });
@ -100,64 +109,73 @@ module.exports = (shepherd) => {
* params: * params:
*/ */
shepherd.get('/update/bins', (req, res, next) => { shepherd.get('/update/bins', (req, res, next) => {
const rootLocation = shepherd.path.join(__dirname, '../../'); if (shepherd.checkToken(req.query.token)) {
const _os = shepherd.os.platform(); const rootLocation = shepherd.path.join(__dirname, '../../');
const successObj = { const _os = shepherd.os.platform();
msg: 'success', const successObj = {
result: { msg: 'success',
filesCount: binsToUpdate.length, result: {
list: binsToUpdate, filesCount: binsToUpdate.length,
}, list: binsToUpdate,
}; },
};
res.end(JSON.stringify(successObj));
res.end(JSON.stringify(successObj));
for (let i = 0; i < binsToUpdate.length; i++) {
shepherd.downloadFile({ for (let i = 0; i < binsToUpdate.length; i++) {
remoteFile: remoteBinLocation[_os] + binsToUpdate[i].name, shepherd.downloadFile({
localFile: `${rootLocation}${localBinLocation[_os]}patch/${binsToUpdate[i].name}`, remoteFile: remoteBinLocation[_os] + binsToUpdate[i].name,
onProgress: (received, total) => { localFile: `${rootLocation}${localBinLocation[_os]}patch/${binsToUpdate[i].name}`,
const percentage = (received * 100) / total; onProgress: (received, total) => {
const percentage = (received * 100) / total;
if (percentage.toString().indexOf('.10') > -1) {
if (percentage.toString().indexOf('.10') > -1) {
shepherd.io.emit('patch', {
msg: {
type: 'bins-update',
status: 'progress',
file: binsToUpdate[i].name,
bytesTotal: total,
bytesReceived: received,
},
});
// shepherd.log(`${binsToUpdate[i].name} ${percentage}% | ${received} bytes out of ${total} bytes.`);
}
}
})
.then(() => {
// verify that remote file is matching to DL'ed file
const localBinSize = shepherd.fs.statSync(`${rootLocation}${localBinLocation[_os]}patch/${binsToUpdate[i].name}`).size;
shepherd.log('compare dl file size');
if (localBinSize === binsToUpdate[i].rSize) {
shepherd.io.emit('patch', { shepherd.io.emit('patch', {
msg: { msg: {
type: 'bins-update', type: 'bins-update',
status: 'progress',
file: binsToUpdate[i].name, file: binsToUpdate[i].name,
bytesTotal: total, status: 'done',
bytesReceived: received,
}, },
}); });
// shepherd.log(`${binsToUpdate[i].name} ${percentage}% | ${received} bytes out of ${total} bytes.`); shepherd.log(`file ${binsToUpdate[i].name} succesfully downloaded`);
} else {
shepherd.io.emit('patch', {
msg: {
type: 'bins-update',
file: binsToUpdate[i].name,
message: 'size mismatch',
},
});
shepherd.log(`error: ${binsToUpdate[i].name} file size doesnt match remote!`);
} }
} });
}) }
.then(() => { } else {
// verify that remote file is matching to DL'ed file const errorObj = {
const localBinSize = shepherd.fs.statSync(`${rootLocation}${localBinLocation[_os]}patch/${binsToUpdate[i].name}`).size; msg: 'error',
shepherd.log('compare dl file size'); result: 'unauthorized access',
};
if (localBinSize === binsToUpdate[i].rSize) {
shepherd.io.emit('patch', { res.end(JSON.stringify(errorObj));
msg: {
type: 'bins-update',
file: binsToUpdate[i].name,
status: 'done',
},
});
shepherd.log(`file ${binsToUpdate[i].name} succesfully downloaded`);
} else {
shepherd.io.emit('patch', {
msg: {
type: 'bins-update',
file: binsToUpdate[i].name,
message: 'size mismatch',
},
});
shepherd.log(`error: ${binsToUpdate[i].name} file size doesnt match remote!`);
}
});
} }
}); });

147
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) => {
const successObj = { if (shepherd.checkToken(req.query.token)) {
msg: 'success', const successObj = {
result: 'dl started' msg: 'success',
}; 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,51 +94,60 @@ module.exports = (shepherd) => {
* params: * params:
*/ */
shepherd.get('/update/patch/check', (req, res, next) => { shepherd.get('/update/patch/check', (req, res, next) => {
const rootLocation = shepherd.path.join(__dirname, '../../'); if (shepherd.checkToken(req.query.token)) {
const options = { const rootLocation = shepherd.path.join(__dirname, '../../');
url: 'https://github.com/pbca26/dl-test/raw/master/version', const options = {
method: 'GET', url: 'https://github.com/pbca26/dl-test/raw/master/version',
}; method: 'GET',
};
shepherd.request(options, (error, response, body) => {
if (response && shepherd.request(options, (error, response, body) => {
response.statusCode && if (response &&
response.statusCode === 200) { response.statusCode &&
const remoteVersion = body.split('\n'); response.statusCode === 200) {
const localVersionFile = shepherd.fs.readFileSync(`${rootLocation}version`, 'utf8'); const remoteVersion = body.split('\n');
let localVersion; const localVersionFile = shepherd.fs.readFileSync(`${rootLocation}version`, 'utf8');
let localVersion;
if (localVersionFile.indexOf('\r\n') > -1) {
localVersion = localVersionFile.split('\r\n'); if (localVersionFile.indexOf('\r\n') > -1) {
} else { localVersion = localVersionFile.split('\r\n');
localVersion = localVersionFile.split('\n'); } else {
} localVersion = localVersionFile.split('\n');
}
if (remoteVersion[0] === localVersion[0]) {
const successObj = {
msg: 'success',
result: 'latest',
};
res.end(JSON.stringify(successObj)); if (remoteVersion[0] === localVersion[0]) {
const successObj = {
msg: 'success',
result: 'latest',
};
res.end(JSON.stringify(successObj));
} else {
const successObj = {
msg: 'success',
result: 'update',
version: {
local: localVersion[0],
remote: remoteVersion[0],
},
};
res.end(JSON.stringify(successObj));
}
} else { } else {
const successObj = { res.end({
msg: 'success', err: 'error getting update',
result: 'update', });
version: {
local: localVersion[0],
remote: remoteVersion[0],
},
};
res.end(JSON.stringify(successObj));
} }
} else { });
res.end({ } else {
err: 'error getting update', const errorObj = {
}); msg: 'error',
} result: 'unauthorized access',
}); };
res.end(JSON.stringify(errorObj));
}
}); });
/* /*
@ -138,16 +156,25 @@ module.exports = (shepherd) => {
* params: * params:
*/ */
shepherd.get('/unpack', (req, res, next) => { shepherd.get('/unpack', (req, res, next) => {
const dlLocation = shepherd.path.join(__dirname, '../../'); if (shepherd.checkToken(req.query.token)) {
const zip = new shepherd.AdmZip(`${dlLocation}patch.zip`); const dlLocation = shepherd.path.join(__dirname, '../../');
zip.extractAllTo(/*target path*/ `${dlLocation}/patch/unpack`, /*overwrite*/true); const zip = new shepherd.AdmZip(`${dlLocation}patch.zip`);
zip.extractAllTo(/*target path*/ `${dlLocation}/patch/unpack`, /*overwrite*/true);
const successObj = {
msg: 'success', const successObj = {
result: 'unpack started', msg: 'success',
}; result: 'unpack started',
};
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;

107
routes/shepherd/downloadZcparams.js

@ -67,66 +67,75 @@ module.exports = (shepherd) => {
* params: * params:
*/ */
shepherd.get('/zcparamsdl', (req, res, next) => { shepherd.get('/zcparamsdl', (req, res, next) => {
// const dlLocation = shepherd.zcashParamsDir + '/test'; if (shepherd.checkToken(req.query.token)) {
const dlLocation = shepherd.zcashParamsDir; // const dlLocation = shepherd.zcashParamsDir + '/test';
const dlOption = req.query.dloption; const dlLocation = shepherd.zcashParamsDir;
const dlOption = req.query.dloption;
const successObj = { const successObj = {
msg: 'success', msg: 'success',
result: 'zcash params dl started', result: 'zcash params dl started',
}; };
res.end(JSON.stringify(successObj));
res.end(JSON.stringify(successObj)); for (let key in shepherd.zcashParamsDownloadLinks[dlOption]) {
shepherd.downloadFile({
remoteFile: shepherd.zcashParamsDownloadLinks[dlOption][key],
localFile: `${dlLocation}/sprout-${key}.key`,
onProgress: (received, total) => {
const percentage = (received * 100) / total;
if (percentage.toString().indexOf('.10') > -1) {
shepherd.io.emit('zcparams', {
msg: {
type: 'zcpdownload',
status: 'progress',
file: key,
bytesTotal: total,
bytesReceived: received,
progress: percentage,
},
});
// shepherd.log(`${key} ${percentage}% | ${received} bytes out of ${total} bytes.`);
}
}
})
.then(() => {
const checkZcashParams = shepherd.zcashParamsExist();
for (let key in shepherd.zcashParamsDownloadLinks[dlOption]) { shepherd.log(`${key} dl done`);
shepherd.downloadFile({
remoteFile: shepherd.zcashParamsDownloadLinks[dlOption][key],
localFile: `${dlLocation}/sprout-${key}.key`,
onProgress: (received, total) => {
const percentage = (received * 100) / total;
if (percentage.toString().indexOf('.10') > -1) { if (checkZcashParams.error) {
shepherd.io.emit('zcparams', { shepherd.io.emit('zcparams', {
msg: { msg: {
type: 'zcpdownload', type: 'zcpdownload',
status: 'progress',
file: key, file: key,
bytesTotal: total, status: 'error',
bytesReceived: received, message: 'size mismatch',
progress: percentage, progress: 100,
}, },
}); });
// shepherd.log(`${key} ${percentage}% | ${received} bytes out of ${total} bytes.`); } else {
shepherd.io.emit('zcparams', {
msg: {
type: 'zcpdownload',
file: key,
progress: 100,
status: 'done',
},
});
shepherd.log(`file ${key} succesfully downloaded`);
} }
} });
}) }
.then(() => { } else {
const checkZcashParams = shepherd.zcashParamsExist(); const errorObj = {
msg: 'error',
shepherd.log(`${key} dl done`); result: 'unauthorized access',
};
if (checkZcashParams.error) {
shepherd.io.emit('zcparams', { res.end(JSON.stringify(errorObj));
msg: {
type: 'zcpdownload',
file: key,
status: 'error',
message: 'size mismatch',
progress: 100,
},
});
} else {
shepherd.io.emit('zcparams', {
msg: {
type: 'zcpdownload',
file: key,
progress: 100,
status: 'done',
},
});
shepherd.log(`file ${key} succesfully downloaded`);
}
});
} }
}); });

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;
}

139
routes/shepherd/electrum/auth.js

@ -1,64 +1,117 @@
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;
const _seed = req.body.seed;
let keys; for (let key in shepherd.electrumCoins) {
if (key !== 'auth') {
if ((_seed.length === 51 || _seed.length === 52) && const _abbr = key;
_seed.indexOf(' ') === -1 && const _seed = req.body.seed;
_seed.match(/^[a-zA-Z0-9]*$/)) { let keys;
let key = shepherd.bitcoinJS.ECPair.fromWIF(_seed, shepherd.electrumJSNetworks.komodo); let isWif = false;
keys = {
priv: key.toWIF(), if (req.body.seed.match('^[a-zA-Z0-9]{34}$') &&
pub: key.getAddress(), shepherd.appConfig.experimentalFeatures) {
}; shepherd.log('watchonly pub addr');
} else { shepherd.electrumKeys[_abbr] = {
keys = shepherd.seedToWif(_seed, shepherd.findNetworkObj(_abbr), req.body.iguana); priv: req.body.seed,
pub: req.body.seed,
};
} else {
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);
keys = {
priv: key.toWIF(),
pub: key.getAddress(),
};
} catch (e) {
_wifError = true;
break;
}
} else {
keys = shepherd.seedToWif(_seed, shepherd.findNetworkObj(_abbr), req.body.iguana);
}
shepherd.electrumKeys[_abbr] = {
priv: keys.priv,
pub: keys.pub,
};
}
}
} }
shepherd.electrumKeys[_abbr] = { shepherd.electrumCoins.auth = true;
priv: keys.priv,
pub: keys.pub,
};
}
shepherd.electrumCoins.auth = true; // shepherd.log(JSON.stringify(shepherd.electrumKeys, null, '\t'), true);
// shepherd.log(JSON.stringify(shepherd.electrumKeys, null, '\t'), true); const successObj = {
msg: _wifError ? 'error' : 'success',
result: 'true',
};
const successObj = { res.end(JSON.stringify(successObj));
msg: 'success', } else {
result: 'true', const errorObj = {
}; msg: 'error',
result: 'unauthorized access',
};
res.end(JSON.stringify(successObj)); res.end(JSON.stringify(errorObj));
}
}); });
shepherd.post('/electrum/lock', (req, res, next) => { shepherd.post('/electrum/lock', (req, res, next) => {
shepherd.electrumCoins.auth = false; if (shepherd.checkToken(req.body.token)) {
shepherd.electrumKeys = {}; shepherd.electrumCoins.auth = false;
shepherd.electrumKeys = {};
const successObj = { const successObj = {
msg: 'success', msg: 'success',
result: 'true', result: 'true',
}; };
res.end(JSON.stringify(successObj));
} else {
const errorObj = {
msg: 'error',
result: 'unauthorized access',
};
res.end(JSON.stringify(successObj)); res.end(JSON.stringify(errorObj));
}
}); });
shepherd.post('/electrum/logout', (req, res, next) => { shepherd.post('/electrum/logout', (req, res, next) => {
shepherd.electrumCoins = { if (shepherd.checkToken(req.body.token)) {
auth: false, shepherd.electrumCoins = {
}; auth: false,
shepherd.electrumKeys = {}; };
shepherd.electrumKeys = {};
const obj = { const obj = {
msg: 'success', msg: 'success',
result: 'result', result: 'result',
}; };
res.end(JSON.stringify(obj));
} else {
const errorObj = {
msg: 'error',
result: 'unauthorized access',
};
res.end(JSON.stringify(obj)); res.end(JSON.stringify(errorObj));
}
}); });
return shepherd; return shepherd;

203
routes/shepherd/electrum/balance.js

@ -1,68 +1,87 @@
module.exports = (shepherd) => { module.exports = (shepherd) => {
shepherd.get('/electrum/getbalance', (req, res, next) => { shepherd.get('/electrum/getbalance', (req, res, next) => {
const network = req.query.network || shepherd.findNetworkObj(req.query.coin); if (shepherd.checkToken(req.query.token)) {
const ecl = new shepherd.electrumJSCore(shepherd.electrumServers[network].port, shepherd.electrumServers[network].address, shepherd.electrumServers[network].proto); // tcp or tls const network = req.query.network || shepherd.findNetworkObj(req.query.coin);
const ecl = new shepherd.electrumJSCore(shepherd.electrumServers[network].port, shepherd.electrumServers[network].address, shepherd.electrumServers[network].proto); // tcp or tls
shepherd.log('electrum getbalance =>', true);
shepherd.log('electrum getbalance =>', true);
ecl.connect();
ecl.blockchainAddressGetBalance(req.query.address) ecl.connect();
.then((json) => { ecl.blockchainAddressGetBalance(req.query.address)
if (json && .then((json) => {
json.hasOwnProperty('confirmed') && if (json &&
json.hasOwnProperty('unconfirmed')) { json.hasOwnProperty('confirmed') &&
if (network === 'komodo') { json.hasOwnProperty('unconfirmed')) {
ecl.connect(); if (network === 'komodo') {
ecl.blockchainAddressListunspent(req.query.address) ecl.blockchainAddressListunspent(req.query.address)
.then((utxoList) => { .then((utxoList) => {
if (utxoList && if (utxoList &&
utxoList.length) { utxoList.length) {
// filter out < 10 KMD amounts // filter out < 10 KMD amounts
let _utxo = []; let _utxo = [];
for (let i = 0; i < utxoList.length; i++) { for (let i = 0; i < utxoList.length; i++) {
shepherd.log(`utxo ${utxoList[i]['tx_hash']} sats ${utxoList[i].value} value ${Number(utxoList[i].value) * 0.00000001}`, true); shepherd.log(`utxo ${utxoList[i]['tx_hash']} sats ${utxoList[i].value} value ${Number(utxoList[i].value) * 0.00000001}`, true);
if (Number(utxoList[i].value) * 0.00000001 >= 10) { if (Number(utxoList[i].value) * 0.00000001 >= 10) {
_utxo.push(utxoList[i]); _utxo.push(utxoList[i]);
}
} }
}
shepherd.log('filtered utxo list =>', true); shepherd.log('filtered utxo list =>', true);
shepherd.log(_utxo, true); shepherd.log(_utxo, true);
if (_utxo && if (_utxo &&
_utxo.length) { _utxo.length) {
let interestTotal = 0; let interestTotal = 0;
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);
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);
if (decodedTx && if (decodedTx &&
decodedTx.format && decodedTx.format &&
decodedTx.format.locktime > 0) { decodedTx.format.locktime > 0) {
interestTotal += shepherd.kmdCalcInterest(decodedTx.format.locktime, _utxoItem.value); interestTotal += shepherd.kmdCalcInterest(decodedTx.format.locktime, _utxoItem.value);
} }
shepherd.log('decoded tx =>', true); shepherd.log('decoded tx =>', true);
shepherd.log(decodedTx, true); shepherd.log(decodedTx, true);
resolve(true); resolve(true);
});
}); });
}))
.then(promiseResult => {
ecl.close();
const successObj = {
msg: 'success',
result: {
balance: Number((0.00000001 * json.confirmed).toFixed(8)),
unconfirmed: Number((0.00000001 * json.unconfirmed).toFixed(8)),
unconfirmedSats: json.unconfirmed,
balanceSats: json.confirmed,
interest: Number(interestTotal.toFixed(8)),
interestSats: Math.floor(interestTotal * 100000000),
total: interestTotal > 0 ? Number((0.00000001 * json.confirmed + interestTotal).toFixed(8)) : 0,
totalSats: interestTotal > 0 ? json.confirmed + Math.floor(interestTotal * 100000000) : 0,
},
};
res.end(JSON.stringify(successObj));
}); });
})) } else {
.then(promiseResult => {
ecl.close(); ecl.close();
const successObj = { const successObj = {
msg: 'success', msg: 'success',
result: { result: {
@ -70,16 +89,18 @@ module.exports = (shepherd) => {
unconfirmed: Number((0.00000001 * json.unconfirmed).toFixed(8)), unconfirmed: Number((0.00000001 * json.unconfirmed).toFixed(8)),
unconfirmedSats: json.unconfirmed, unconfirmedSats: json.unconfirmed,
balanceSats: json.confirmed, balanceSats: json.confirmed,
interest: Number(interestTotal.toFixed(8)), interest: 0,
interestSats: Math.floor(interestTotal * 100000000), interestSats: 0,
total: interestTotal > 0 ? Number((0.00000001 * json.confirmed + interestTotal).toFixed(8)) : 0, total: 0,
totalSats: interestTotal > 0 ?json.confirmed + Math.floor(interestTotal * 100000000) : 0, totalSats: 0,
}, },
}; };
res.end(JSON.stringify(successObj)); res.end(JSON.stringify(successObj));
}); }
} else { } else {
ecl.close();
const successObj = { const successObj = {
msg: 'success', msg: 'success',
result: { result: {
@ -96,50 +117,44 @@ module.exports = (shepherd) => {
res.end(JSON.stringify(successObj)); res.end(JSON.stringify(successObj));
} }
} else { });
const successObj = { } else {
msg: 'success', ecl.close();
result: { shepherd.log('electrum getbalance ==>', true);
balance: Number((0.00000001 * json.confirmed).toFixed(8)), shepherd.log(json, true);
unconfirmed: Number((0.00000001 * json.unconfirmed).toFixed(8)),
unconfirmedSats: json.unconfirmed, const successObj = {
balanceSats: json.confirmed, msg: 'success',
interest: 0, result: {
interestSats: 0, balance: Number((0.00000001 * json.confirmed).toFixed(8)),
total: 0, unconfirmed: Number((0.00000001 * json.unconfirmed).toFixed(8)),
totalSats: 0, unconfirmedSats: json.unconfirmed,
}, balanceSats: json.confirmed,
}; },
};
res.end(JSON.stringify(successObj));
} res.end(JSON.stringify(successObj));
}); }
} else { } else {
ecl.close(); ecl.close();
shepherd.log('electrum getbalance ==>', true);
shepherd.log(json, true);
const successObj = { const successObj = {
msg: 'success', msg: 'error',
result: { result: shepherd.CONNECTION_ERROR_OR_INCOMPLETE_DATA,
balance: Number((0.00000001 * json.confirmed).toFixed(8)),
unconfirmed: Number((0.00000001 * json.unconfirmed).toFixed(8)),
unconfirmedSats: json.unconfirmed,
balanceSats: json.confirmed,
},
}; };
res.end(JSON.stringify(successObj)); res.end(JSON.stringify(successObj));
} }
} else { });
const successObj = { } else {
msg: 'error', const errorObj = {
result: shepherd.CONNECTION_ERROR_OR_INCOMPLETE_DATA, msg: 'error',
}; result: 'unauthorized access',
};
res.end(JSON.stringify(successObj));
} res.end(JSON.stringify(errorObj));
}); }
}); });
return shepherd; return shepherd;
}; };

46
routes/shepherd/electrum/block.js

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

81
routes/shepherd/electrum/coins.js

@ -54,37 +54,84 @@ 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/coins/add', (req, res, next) => { shepherd.get('/electrum/coin/changepub', (req, res, next) => {
const result = shepherd.addElectrumCoin(req.query.coin); if (shepherd.checkToken(req.query.token)) {
shepherd.electrumKeys[req.query.coin].pub = req.query.pub;
const successObj = { const successObj = {
msg: 'success', msg: 'success',
result, result: 'true',
}; };
res.end(JSON.stringify(successObj));
} else {
const errorObj = {
msg: 'error',
result: 'unauthorized access',
};
res.end(JSON.stringify(errorObj));
}
});
res.end(JSON.stringify(successObj)); shepherd.get('/electrum/coins/add', (req, res, next) => {
if (shepherd.checkToken(req.query.token)) {
const result = shepherd.addElectrumCoin(req.query.coin);
const successObj = {
msg: 'success',
result,
};
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) => {
let _electrumCoins = JSON.parse(JSON.stringify(shepherd.electrumCoins)); // deep cloning if (shepherd.checkToken(req.query.token)) {
let _electrumCoins = JSON.parse(JSON.stringify(shepherd.electrumCoins)); // deep cloning
for (let key in _electrumCoins) { for (let key in _electrumCoins) {
if (shepherd.electrumKeys[key]) { if (shepherd.electrumKeys[key]) {
_electrumCoins[key].pub = shepherd.electrumKeys[key].pub; _electrumCoins[key].pub = shepherd.electrumKeys[key].pub;
}
} }
}
const successObj = { const successObj = {
msg: 'success', msg: 'success',
result: _electrumCoins, result: _electrumCoins,
}; };
res.end(JSON.stringify(successObj));
} else {
const errorObj = {
msg: 'error',
result: 'unauthorized access',
};
res.end(JSON.stringify(successObj)); 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;
};

792
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;
shepherd.log('buildSignedTx'); // 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', 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,10 +75,25 @@ module.exports = (shepherd) => {
tx.addInput(utxo[i].txid, utxo[i].vout); tx.addInput(utxo[i].txid, utxo[i].vout);
} }
tx.addOutput(sendTo, Number(spendValue)); if (shepherd.isPos(network)) {
tx.addOutput(sendTo, Number(spendValue), shepherd.getNetworkData(network));
} else {
tx.addOutput(sendTo, Number(spendValue));
}
if (changeValue > 0) { if (changeValue > 0) {
tx.addOutput(changeAddress, Number(changeValue)); if (shepherd.isPos(network)) {
tx.addOutput(changeAddress, Number(changeValue), shepherd.getNetworkData(network));
} else {
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' ||
@ -75,7 +111,65 @@ 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++) {
tx.sign(i, key); 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;
}
// 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,386 +195,424 @@ 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)) {
const network = req.query.network || shepherd.findNetworkObj(req.query.coin); // TODO: unconf output(s) error message
const ecl = new shepherd.electrumJSCore(shepherd.electrumServers[network].port, shepherd.electrumServers[network].address, shepherd.electrumServers[network].proto); // tcp or tls const network = req.query.network || shepherd.findNetworkObj(req.query.coin);
const outputAddress = req.query.address; const ecl = new shepherd.electrumJSCore(shepherd.electrumServers[network].port, shepherd.electrumServers[network].address, shepherd.electrumServers[network].proto); // tcp or tls
const changeAddress = req.query.change; const outputAddress = req.query.address;
let value = Number(req.query.value); const changeAddress = req.query.change;
const push = req.query.push; const push = req.query.push;
const fee = shepherd.electrumServers[network].txfee; const opreturn = req.query.opreturn;
let wif = req.query.wif; const btcFee = req.query.btcfee ? Number(req.query.btcfee) : null;
let fee = shepherd.electrumServers[network].txfee;
if (req.query.gui) { let value = Number(req.query.value);
wif = shepherd.electrumKeys[req.query.coin].priv; let wif = req.query.wif;
}
if (req.query.gui) {
wif = shepherd.electrumKeys[req.query.coin].priv;
}
shepherd.log('electrum createrawtx =>', true); if (req.query.vote) {
wif = shepherd.elections.priv;
ecl.connect(); }
shepherd.listunspent(ecl, changeAddress, network, true, true)
.then((utxoList) => { if (btcFee) {
ecl.close(); fee = 0;
}
if (utxoList &&
utxoList.length) { shepherd.log('electrum createrawtx =>', true);
let utxoListFormatted = [];
let totalInterest = 0; ecl.connect();
let totalInterestUTXOCount = 0; shepherd.listunspent(ecl, changeAddress, network, true, req.query.verify === 'true' ? true : null)
let interestClaimThreshold = 200; .then((utxoList) => {
let utxoVerified = true; ecl.close();
for (let i = 0; i < utxoList.length; i++) { if (utxoList &&
if (network === 'komodo') { utxoList.length &&
utxoListFormatted.push({ utxoList[0] &&
txid: utxoList[i].txid, utxoList[0].txid) {
vout: utxoList[i].vout, let utxoListFormatted = [];
value: Number(utxoList[i].amountSats), let totalInterest = 0;
interestSats: Number(utxoList[i].interestSats), let totalInterestUTXOCount = 0;
verified: utxoList[i].verified ? utxoList[i].verified : false, let interestClaimThreshold = 200;
}); let utxoVerified = true;
} else {
utxoListFormatted.push({ for (let i = 0; i < utxoList.length; i++) {
txid: utxoList[i].txid, if (network === 'komodo') {
vout: utxoList[i].vout, utxoListFormatted.push({
value: Number(utxoList[i].amountSats), txid: utxoList[i].txid,
verified: utxoList[i].verified ? utxoList[i].verified : false, 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('electrum listunspent unformatted ==>', true);
shepherd.log(utxoList, true); shepherd.log(utxoList, true);
shepherd.log('electrum listunspent formatted ==>', true); shepherd.log('electrum listunspent formatted ==>', true);
shepherd.log(utxoListFormatted, true); shepherd.log(utxoListFormatted, true);
const _maxSpendBalance = Number(shepherd.maxSpendBalance(utxoListFormatted)); const _maxSpendBalance = Number(shepherd.maxSpendBalance(utxoListFormatted));
let targets = [{ let targets = [{
address: outputAddress, address: outputAddress,
value: value > _maxSpendBalance ? _maxSpendBalance : value, value: value > _maxSpendBalance ? _maxSpendBalance : value,
}]; }];
shepherd.log('targets =>', true); shepherd.log('targets =>', true);
shepherd.log(targets, true);
const feeRate = 20; // sats/byte
// default coin selection algo blackjack with fallback to accumulative
// make a first run, calc approx tx fee
// if ins and outs are empty reduce max spend by txfee
let { inputs, outputs, fee } = shepherd.coinSelect(utxoListFormatted, targets, feeRate);
shepherd.log('coinselect res =>', true);
shepherd.log('coinselect inputs =>', true);
shepherd.log(inputs, true);
shepherd.log('coinselect outputs =>', true);
shepherd.log(outputs, true);
shepherd.log('coinselect calculated fee =>', true);
shepherd.log(fee, true);
if (!outputs) {
targets[0].value = targets[0].value - fee;
shepherd.log('second run', true);
shepherd.log('coinselect adjusted targets =>', true);
shepherd.log(targets, true); shepherd.log(targets, true);
const secondRun = shepherd.coinSelect(utxoListFormatted, targets, feeRate); targets[0].value = targets[0].value + fee;
inputs = secondRun.inputs;
outputs = secondRun.outputs;
fee = secondRun.fee;
shepherd.log('second run coinselect inputs =>', true); 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(inputs, true);
shepherd.log('second run coinselect outputs =>', true); shepherd.log('coinselect outputs =>', true);
shepherd.log(outputs, true); shepherd.log(outputs, true);
shepherd.log('second run coinselect fee =>', true); shepherd.log('coinselect calculated fee =>', true);
shepherd.log(fee, true); shepherd.log(fee, true);
}
let _change = 0; 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);
}
if (outputs && let _change = 0;
outputs.length === 2) {
_change = outputs[1].value;
}
// check if any outputs are unverified if (outputs &&
if (inputs && outputs.length === 2) {
inputs.length) { _change = outputs[1].value - fee;
for (let i = 0; i < inputs.length; i++) {
if (!inputs[i].verified) {
utxoVerified = false;
break;
}
} }
for (let i = 0; i < inputs.length; i++) { if (!btcFee &&
if (Number(inputs[i].interestSats) > interestClaimThreshold) { _change === 0) {
totalInterest += Number(inputs[i].interestSats); outputs[0].value = outputs[0].value - fee;
totalInterestUTXOCount++; }
if (btcFee) {
value = outputs[0].value;
} else {
if (_change > 0) {
value = outputs[0].value - fee;
} }
} }
}
const _maxSpend = shepherd.maxSpendBalance(utxoListFormatted); shepherd.log('adjusted outputs, value - default fee =>', true);
shepherd.log(outputs, true);
if (value > _maxSpend) { // check if any outputs are unverified
const successObj = { if (inputs &&
msg: 'error', inputs.length) {
result: `Spend value is too large. Max available amount is ${Number((_maxSpend * 0.00000001.toFixed(8)))}`, for (let i = 0; i < inputs.length; i++) {
}; if (!inputs[i].verified) {
utxoVerified = false;
break;
}
}
res.end(JSON.stringify(successObj)); for (let i = 0; i < inputs.length; i++) {
} else { if (Number(inputs[i].interestSats) > interestClaimThreshold) {
shepherd.log(`maxspend ${_maxSpend} (${_maxSpend * 0.00000001})`, true); totalInterest += Number(inputs[i].interestSats);
shepherd.log(`value ${value}`, true); totalInterestUTXOCount++;
shepherd.log(`sendto ${outputAddress} amount ${value} (${value * 0.00000001})`, true);
shepherd.log(`changeto ${changeAddress} amount ${_change} (${_change * 0.00000001})`, true);
// account for KMD interest
if (network === 'komodo' &&
totalInterest > 0) {
// account for extra vout
const _feeOverhead = outputs.length === 1 ? shepherd.estimateTxSize(0, 1) * feeRate : 0;
shepherd.log(`max interest to claim ${totalInterest} (${totalInterest * 0.00000001})`, true);
shepherd.log(`estimated fee overhead ${_feeOverhead}`, true);
shepherd.log(`current change amount ${_change} (${_change * 0.00000001}), boosted change amount ${_change + (totalInterest - _feeOverhead)} (${(_change + (totalInterest - _feeOverhead)) * 0.00000001})`, true);
if (_maxSpend === 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 && const _maxSpend = shepherd.maxSpendBalance(utxoListFormatted);
!outputs) {
if (value > _maxSpend) {
const successObj = { const successObj = {
msg: 'error', msg: 'error',
result: 'Can\'t find best fit utxo. Try lower amount.', result: `Spend value is too large. Max available amount is ${Number((_maxSpend * 0.00000001.toFixed(8)))}`,
}; };
res.end(JSON.stringify(successObj)); res.end(JSON.stringify(successObj));
} else { } else {
let vinSum = 0; shepherd.log(`maxspend ${_maxSpend} (${_maxSpend * 0.00000001})`, true);
shepherd.log(`value ${value}`, true);
for (let i = 0; i < inputs.length; i++) { shepherd.log(`sendto ${outputAddress} amount ${value} (${value * 0.00000001})`, true);
vinSum += inputs[i].value; 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);
}
} }
const _estimatedFee = vinSum - outputs[0].value - _change; if (!inputs &&
!outputs) {
const successObj = {
msg: 'error',
result: 'Can\'t find best fit utxo. Try lower amount.',
};
shepherd.log(`vin sum ${vinSum} (${vinSum * 0.00000001})`, true); res.end(JSON.stringify(successObj));
shepherd.log(`estimatedFee ${_estimatedFee} (${_estimatedFee * 0.00000001})`, true); } else {
let vinSum = 0;
let _rawtx; for (let i = 0; i < inputs.length; i++) {
vinSum += inputs[i].value;
}
if (req.query.unsigned) { const _estimatedFee = vinSum - outputs[0].value - _change;
_rawtx = shepherd.buildUnsignedTx(
outputAddress,
changeAddress,
network,
inputs,
_change,
value
);
} else {
_rawtx = shepherd.buildSignedTx(
outputAddress,
changeAddress,
wif,
network,
inputs,
_change,
value
);
}
if (!push || shepherd.log(`vin sum ${vinSum} (${vinSum * 0.00000001})`, true);
push === 'false') { shepherd.log(`estimatedFee ${_estimatedFee} (${_estimatedFee * 0.00000001})`, true);
const successObj = { // double check no extra fee is applied
msg: 'success', shepherd.log(`vin - vout ${vinSum - value - _change}`);
result: {
utxoSet: inputs, if ((vinSum - value - _change) > fee) {
change: _change, _change += fee;
changeAdjusted: _change, shepherd.log(`double fee, increase change by ${fee}`);
totalInterest, }
// wif,
fee, // TODO: use individual dust thresholds
value, if (_change > 0 &&
_change <= 1000) {
shepherd.log(`change is < 1000 sats, donate ${_change} sats to miners`, true);
_change = 0;
}
let _rawtx;
if (req.query.unsigned) {
_rawtx = shepherd.buildUnsignedTx(
outputAddress, outputAddress,
changeAddress, changeAddress,
network, network,
rawtx: _rawtx, inputs,
utxoVerified, _change,
}, value
}; );
} else {
res.end(JSON.stringify(successObj)); if (!req.query.offline) {
} else { if (network === 'btg' ||
const ecl = new shepherd.electrumJSCore(shepherd.electrumServers[network].port, shepherd.electrumServers[network].address, shepherd.electrumServers[network].proto); // tcp or tls network === 'bch') {
_rawtx = shepherd.buildSignedTxForks(
ecl.connect(); outputAddress,
ecl.blockchainTransactionBroadcast(_rawtx) changeAddress,
.then((txid) => { wif,
ecl.close(); network,
inputs,
if (txid && _change,
txid.indexOf('bad-txns-inputs-spent') > -1) { value
const successObj = { );
msg: 'error', } else {
result: 'Bad transaction inputs spent', _rawtx = shepherd.buildSignedTx(
raw: {
utxoSet: inputs,
change: _change,
changeAdjusted: _change,
totalInterest,
fee,
value,
outputAddress, outputAddress,
changeAddress, changeAddress,
wif,
network, network,
rawtx: _rawtx, inputs,
txid, _change,
utxoVerified, 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,
}; };
res.end(JSON.stringify(successObj));
} else {
if (txid && if (txid &&
txid.length === 64) { txid.indexOf('bad-txns-inputs-spent') > -1) {
if (txid.indexOf('bad-txns-in-belowout') > -1) { const successObj = {
const successObj = { msg: 'error',
msg: 'error', result: 'Bad transaction inputs spent',
result: 'Bad transaction inputs spent', raw: _rawObj,
raw: { };
utxoSet: inputs,
change: _change, res.end(JSON.stringify(successObj));
changeAdjusted: _change,
totalInterest,
fee,
value,
outputAddress,
changeAddress,
network,
rawtx: _rawtx,
txid,
utxoVerified,
},
};
res.end(JSON.stringify(successObj));
} else {
const successObj = {
msg: 'success',
result: {
utxoSet: inputs,
change: _change,
changeAdjusted: _change,
totalInterest,
fee,
// wif,
value,
outputAddress,
changeAddress,
network,
rawtx: _rawtx,
txid,
utxoVerified,
},
};
res.end(JSON.stringify(successObj));
}
} else { } else {
if (txid && if (txid &&
txid.indexOf('bad-txns-in-belowout') > -1) { txid.length === 64) {
const successObj = { if (txid.indexOf('bad-txns-in-belowout') > -1) {
msg: 'error', const successObj = {
result: 'Bad transaction inputs spent', msg: 'error',
raw: { result: 'Bad transaction inputs spent',
utxoSet: inputs, raw: _rawObj,
change: _change, };
changeAdjusted: _change,
totalInterest, res.end(JSON.stringify(successObj));
fee, } else {
value, const successObj = {
outputAddress, msg: 'success',
changeAddress, result: _rawObj,
network, };
rawtx: _rawtx,
txid, res.end(JSON.stringify(successObj));
utxoVerified, }
},
};
res.end(JSON.stringify(successObj));
} else { } else {
const successObj = { if (txid &&
msg: 'error', txid.indexOf('bad-txns-in-belowout') > -1) {
result: 'Can\'t broadcast transaction', const successObj = {
raw: { msg: 'error',
utxoSet: inputs, result: 'Bad transaction inputs spent',
change: _change, raw: _rawObj,
changeAdjusted: _change, };
totalInterest,
fee, res.end(JSON.stringify(successObj));
value, } else {
outputAddress, const successObj = {
changeAddress, msg: 'error',
network, result: 'Can\'t broadcast transaction',
rawtx: _rawtx, raw: _rawObj,
txid, };
utxoVerified,
}, res.end(JSON.stringify(successObj));
}; }
res.end(JSON.stringify(successObj));
} }
} }
} });
}); }
} }
} }
} else {
const successObj = {
msg: 'error',
result: utxoList,
};
res.end(JSON.stringify(successObj));
} }
} else { });
const successObj = { } else {
msg: 'error', const errorObj = {
result: utxoList, msg: 'error',
}; result: 'unauthorized access',
};
res.end(JSON.stringify(successObj)); 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)
.then((json) => { .then((json) => {
ecl.close(); ecl.close();
shepherd.log('electrum pushtx ==>', true); shepherd.log('electrum pushtx ==>', true);
shepherd.log(json, true); shepherd.log(json, true);
const successObj = { const successObj = {
msg: 'success', msg: 'success',
result: json, result: json,
};
res.end(JSON.stringify(successObj));
});
} else {
const errorObj = {
msg: 'error',
result: 'unauthorized access',
}; };
res.end(JSON.stringify(successObj)); res.end(JSON.stringify(errorObj));
}); }
}); });
return shepherd; return shepherd;

31
routes/shepherd/electrum/estimate.js

@ -1,20 +1,29 @@
module.exports = (shepherd) => { module.exports = (shepherd) => {
shepherd.get('/electrum/estimatefee', (req, res, next) => { shepherd.get('/electrum/estimatefee', (req, res, next) => {
const ecl = new shepherd.electrumJSCore(shepherd.electrumServers[req.query.network].port, shepherd.electrumServers[req.query.network].address, shepherd.electrumServers[req.query.network].proto); // tcp or tls 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
ecl.connect(); ecl.connect();
ecl.blockchainEstimatefee(req.query.blocks) ecl.blockchainEstimatefee(req.query.blocks)
.then((json) => { .then((json) => {
ecl.close(); ecl.close();
shepherd.log('electrum estimatefee ==>', true); shepherd.log('electrum estimatefee ==>', true);
const successObj = { const successObj = {
msg: 'success', msg: 'success',
result: json, result: json,
};
res.end(JSON.stringify(successObj));
});
} else {
const errorObj = {
msg: 'error',
result: 'unauthorized access',
}; };
res.end(JSON.stringify(successObj)); 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

353
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,162 +34,258 @@ 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(),
};
const hex = toHexString(bytes); /*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);*/
const key = new shepherd.CoinKey(new Buffer(hex, 'hex'), { return keys;
private: shepherd.getNetworkData(network).wif, }
public: shepherd.getNetworkData(network).pubKeyHash,
});
key.compressed = true; 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 successObj = {
msg: 'success',
result: {
keys,
},
};
res.end(JSON.stringify(successObj));
} else {
const errorObj = {
msg: 'error',
result: 'unauthorized access',
};
// shepherd.log(`seed: ${seed}`, true); res.end(JSON.stringify(errorObj));
// shepherd.log(`network ${network}`, true); }
// shepherd.log(`seedtowif priv key ${key.privateWif}`, true); });
// shepherd.log(`seedtowif pub key ${key.publicAddress}`, true);
return { shepherd.post('/electrum/seedtowif', (req, res, next) => {
priv: key.privateWif, if (shepherd.checkToken(req.body.token)) {
pub: key.publicAddress, let keys = shepherd.seedToWif(req.body.seed, req.body.network.toLowerCase(), req.body.iguana);
};
}
shepherd.get('/electrum/seedtowif', (req, res, next) => { const successObj = {
const keys = shepherd.seedToWif(req.query.seed, req.query.network, req.query.iguana); msg: 'success',
result: {
keys,
},
};
const successObj = { res.end(JSON.stringify(successObj));
msg: 'success', } else {
result: { const errorObj = {
keys, msg: 'error',
}, result: 'unauthorized access',
}; };
res.end(JSON.stringify(successObj)); res.end(JSON.stringify(errorObj));
}
}); });
shepherd.post('/electrum/keys', (req, res, next) => { shepherd.post('/electrum/seedtowif', (req, res, next) => {
let _matchingKeyPairs = 0; if (shepherd.checkToken(req.body.token)) {
let _electrumKeys = {}; let keys = shepherd.seedToWif(req.body.seed, req.body.network.toLowerCase(), req.body.iguana);
for (let key in shepherd.electrumServers) { const successObj = {
const _abbr = shepherd.electrumServers[key].abbr; msg: 'success',
const { priv, pub } = shepherd.seedToWif(req.body.seed, shepherd.findNetworkObj(_abbr), req.body.iguana); result: {
keys,
},
};
if (shepherd.electrumKeys[_abbr].pub === pub && res.end(JSON.stringify(successObj));
shepherd.electrumKeys[_abbr].priv === priv) { } else {
_matchingKeyPairs++; const errorObj = {
} msg: 'error',
result: 'unauthorized access',
};
res.end(JSON.stringify(errorObj));
} }
});
if (req.body.active) { shepherd.getCoinByPub = (address) => {
_electrumKeys = JSON.parse(JSON.stringify(shepherd.electrumKeys)); const _skipNetworks = ['btc', 'crw', 'dgb', 'arg', 'zec', 'nmc', 'ltc', 'vtc', 'via', 'fair', 'doge', 'kmd', 'mona'];
for (let key in _electrumKeys) { try {
if (!shepherd.electrumCoins[key]) { const _b58check = shepherd.isZcash(network.toLowerCase()) ? bitcoinZcash.address.fromBase58Check(address) : bitcoin.address.fromBase58Check(address);
delete _electrumKeys[key]; 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);
} }
} }
} else {
_electrumKeys = shepherd.electrumKeys; if (_coin.length) {
return {
coin: _coin,
version: _b58check.version,
};
} else {
return 'Unable to find matching coin version';
}
} catch(e) {
return 'Invalid pub address';
} }
};
// shepherd.log(JSON.stringify(_electrumKeys, null, '\t'), true); shepherd.addressVersionCheck = (network, address) => {
const _network = shepherd.getNetworkData(network.toLowerCase());
const successObj = { try {
msg: 'success', const _b58check = shepherd.isZcash(network.toLowerCase()) ? bitcoinZcash.address.fromBase58Check(address) : bitcoin.address.fromBase58Check(address);
result: _matchingKeyPairs === Object.keys(shepherd.electrumKeys).length ? _electrumKeys : false,
};
res.end(JSON.stringify(successObj)); if (_b58check.version === _network.pubKeyHash) {
}); return true;
} else {
return false;
}
} catch(e) {
return 'Invalid pub address';
}
};
shepherd.post('/electrum/seed/bip39/match', (req, res, next) => { shepherd.post('/electrum/keys', (req, res, next) => {
const bip39 = require('bip39'); // npm i -S bip39 if (shepherd.checkToken(req.body.token)) {
const crypto = require('crypto'); let _matchingKeyPairs = 0;
const seed = bip39.mnemonicToSeed(req.body.seed); let _totalKeys = 0;
const hdMaster = shepherd.bitcoinJS.HDNode.fromSeedBuffer(seed, shepherd.electrumJSNetworks.komodo); // seed from above let _electrumKeys = {};
const matchPattern = req.body.match; let _seed = req.body.seed;
const _defaultAddressDepth = req.body.addressdepth; let _wifError = false;
const _defaultAccountCount = req.body.accounts;
let _addresses = []; for (let key in shepherd.electrumCoins) {
let _matchingKey; if (key !== 'auth') {
const _abbr = key;
for (let i = 0; i < _defaultAccountCount; i++) { let isWif = false;
for (let j = 0; j < 1; j++) { let priv;
for (let k = 0; k < _defaultAddressDepth; k++) { let pub;
const _key = hdMaster.derivePath(`m/44'/141'/${i}'/${j}/${k}`);
try {
if (_key.keyPair.getAddress() === matchPattern) { bs58check.decode(_seed);
_matchingKey = { isWif = true;
pub: _key.keyPair.getAddress(), } catch (e) {}
priv: _key.keyPair.toWIF(),
}; 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 &&
shepherd.electrumKeys[_abbr].priv === priv) {
_matchingKeyPairs++;
} }
/*_addresses.push({ _totalKeys++;
pub: _key.keyPair.getAddress(),
priv: _key.keyPair.toWIF(),
});*/
} }
} }
}
const successObj = {
msg: 'success',
result: _matchingKey ? _matchingKey : 'address is not found',
};
res.end(JSON.stringify(successObj)); if (req.body.active) {
}); _electrumKeys = JSON.parse(JSON.stringify(shepherd.electrumKeys));
// spv v2 for (let key in _electrumKeys) {
/*shepherd.get('/electrum/bip39/seed', (req, res, next) => { if (!shepherd.electrumCoins[key]) {
const _seed = ''; delete _electrumKeys[key];
// TODO }
const bip39 = require('bip39'); // npm i -S bip39 }
const crypto = require('crypto'); } else {
_electrumKeys = shepherd.electrumKeys;
}
// what you describe as 'seed' const successObj = {
const randomBytes = crypto.randomBytes(16); // 128 bits is enough msg: _wifError ? 'error' : 'success',
result: _wifError ? false : (_matchingKeyPairs === _totalKeys ? _electrumKeys : false),
};
// your 12 word phrase res.end(JSON.stringify(successObj));
const mnemonic = bip39.entropyToMnemonic(randomBytes.toString('hex')); } else {
const errorObj = {
msg: 'error',
result: 'unauthorized access',
};
// what is accurately described as the wallet seed res.end(JSON.stringify(errorObj));
// var seed = bip39.mnemonicToSeed(mnemonic) // you'll use this in #3 below }
const seed = bip39.mnemonicToSeed(_seed); });
console.log(seed); shepherd.getSpvFees = () => {
let _fees = {};
const successObj = { for (let key in shepherd.electrumServers) {
msg: 'success', if (shepherd.electrumServers[key].txfee) {
result: { _fees[shepherd.electrumServers[key].abbr] = shepherd.electrumServers[key].txfee;
servers: shepherd.electrumServers, }
}, }
};
res.end(JSON.stringify(successObj)); return _fees;
};
console.log(shepherd.bitcoinJS.networks.komodo); shepherd.post('/electrum/seed/bip39/match', (req, res, next) => {
const hdMaster = shepherd.bitcoinJS.HDNode.fromSeedBuffer(seed, shepherd.electrumJSNetworks.komodo); // seed from above if (shepherd.checkToken(req.body.token)) {
const seed = bip39.mnemonicToSeed(req.body.seed);
const hdMaster = bitcoin.HDNode.fromSeedBuffer(seed, shepherd.electrumJSNetworks.komodo);
const matchPattern = req.body.match;
const _defaultAddressDepth = req.body.addressdepth;
const _defaultAccountCount = req.body.accounts;
let _addresses = [];
let _matchingKey;
for (let i = 0; i < _defaultAccountCount; i++) {
for (let j = 0; j < 1; j++) {
for (let k = 0; k < _defaultAddressDepth; k++) {
const _key = hdMaster.derivePath(`m/44'/141'/${i}'/${j}/${k}`);
if (_key.keyPair.getAddress() === matchPattern) {
_matchingKey = {
pub: _key.keyPair.getAddress(),
priv: _key.keyPair.toWIF(),
};
}
}
}
}
const key1 = hdMaster.derivePath("m/44'/141'/0'/0/0"); const successObj = {
const key2 = hdMaster.derivePath('m/1'); msg: 'success',
console.log(hdMaster); result: _matchingKey ? _matchingKey : 'address is not found',
};
console.log(key1.keyPair.toWIF()); res.end(JSON.stringify(successObj));
console.log(key1.keyPair.getAddress()); } else {
console.log(key2.keyPair.toWIF()); const errorObj = {
msg: 'error',
result: 'unauthorized access',
};
const hdnode = shepherd.bitcoinJS.HDNode.fromSeedBuffer(seed, shepherd.electrumJSNetworks.komodo).deriveHardened(0).derive(0).derive(1); res.end(JSON.stringify(errorObj));
console.log(`address: ${hdnode.getAddress()}`); }
console.log(`priv (WIF): ${hdnode.keyPair.toWIF()}`); });
});*/
return shepherd; return shepherd;
}; };

89
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,40 +159,50 @@ module.exports = (shepherd) => {
} }
shepherd.get('/electrum/listunspent', (req, res, next) => { shepherd.get('/electrum/listunspent', (req, res, next) => {
const network = req.query.network || shepherd.findNetworkObj(req.query.coin); if (shepherd.checkToken(req.query.token)) {
const ecl = new shepherd.electrumJSCore(shepherd.electrumServers[network].port, shepherd.electrumServers[network].address, shepherd.electrumServers[network].proto); // tcp or tls const network = req.query.network || shepherd.findNetworkObj(req.query.coin);
const ecl = new shepherd.electrumJSCore(shepherd.electrumServers[network].port, shepherd.electrumServers[network].address, shepherd.electrumServers[network].proto); // tcp or tls
if (req.query.full &&
req.query.full === 'true') { if (req.query.full &&
shepherd.listunspent( req.query.full === 'true') {
ecl, shepherd.listunspent(
req.query.address, ecl,
network, req.query.address,
true, network,
req.query.verify true,
).then((listunspent) => { req.query.verify
shepherd.log('electrum listunspent ==>', true); )
.then((listunspent) => {
const successObj = { shepherd.log('electrum listunspent ==>', true);
msg: 'success',
result: listunspent, const successObj = {
}; msg: 'success',
result: listunspent,
res.end(JSON.stringify(successObj)); };
});
} else { res.end(JSON.stringify(successObj));
shepherd.listunspent(ecl, req.query.address, network) });
.then((listunspent) => { } else {
ecl.close(); shepherd.listunspent(ecl, req.query.address, network)
shepherd.log('electrum listunspent ==>', true); .then((listunspent) => {
ecl.close();
shepherd.log('electrum listunspent ==>', true);
const successObj = { const successObj = {
msg: 'success', msg: 'success',
result: listunspent, result: listunspent,
}; };
res.end(JSON.stringify(successObj)); res.end(JSON.stringify(successObj));
}); });
}
} else {
const errorObj = {
msg: 'error',
result: 'unauthorized access',
};
res.end(JSON.stringify(errorObj));
} }
}); });

48
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,17 +133,26 @@ module.exports = (shepherd) => {
} }
shepherd.get('/electrum/merkle/verify', (req, res, next) => { shepherd.get('/electrum/merkle/verify', (req, res, next) => {
shepherd.verifyMerkleByCoin(req.query.coin, req.query.txid, req.query.height) if (shepherd.checkToken(req.query.token)) {
.then((verifyMerkleRes) => { shepherd.verifyMerkleByCoin(req.query.coin, req.query.txid, req.query.height)
const successObj = { .then((verifyMerkleRes) => {
msg: 'success', const successObj = {
result: { msg: 'success',
merkleProof: verifyMerkleRes, result: {
}, merkleProof: verifyMerkleRes,
},
};
res.end(JSON.stringify(successObj));
});
} else {
const errorObj = {
msg: 'error',
result: 'unauthorized access',
}; };
res.end(JSON.stringify(successObj)); res.end(JSON.stringify(errorObj));
}); }
}); });
return shepherd; return shepherd;

199
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,85 +108,112 @@ module.exports = (shepherd) => {
} }
shepherd.get('/electrum/servers', (req, res, next) => { shepherd.get('/electrum/servers', (req, res, next) => {
if (req.query.abbr) { if (shepherd.checkToken(req.query.token)) {
let _electrumServers = {}; if (req.query.abbr) {
let _electrumServers = {};
for (let key in shepherd.electrumServers) { for (let key in shepherd.electrumServers) {
_electrumServers[shepherd.electrumServers[key].abbr] = shepherd.electrumServers[key]; _electrumServers[shepherd.electrumServers[key].abbr] = shepherd.electrumServers[key];
} }
const successObj = { const successObj = {
msg: 'success', msg: 'success',
result: { result: {
servers: _electrumServers, servers: _electrumServers,
}, },
}; };
res.end(JSON.stringify(successObj)); res.end(JSON.stringify(successObj));
} else {
const successObj = {
msg: 'success',
result: {
servers: shepherd.electrumServers,
},
};
res.end(JSON.stringify(successObj));
}
} else { } else {
const successObj = { const errorObj = {
msg: 'success', msg: 'error',
result: { result: 'unauthorized access',
servers: shepherd.electrumServers,
},
}; };
res.end(JSON.stringify(successObj)); res.end(JSON.stringify(errorObj));
} }
}); });
shepherd.get('/electrum/coins/server/set', (req, res, next) => { shepherd.get('/electrum/coins/server/set', (req, res, next) => {
shepherd.electrumCoins[req.query.coin].server = { if (shepherd.checkToken(req.query.token)) {
ip: req.query.address, shepherd.electrumCoins[req.query.coin].server = {
port: req.query.port, ip: req.query.address,
}; port: req.query.port,
};
for (let key in shepherd.electrumServers) { for (let key in shepherd.electrumServers) {
if (shepherd.electrumServers[key].abbr === req.query.coin) { // a bit risky if (shepherd.electrumServers[key].abbr === req.query.coin) { // a bit risky
shepherd.electrumServers[key].address = req.query.address; shepherd.electrumServers[key].address = req.query.address;
shepherd.electrumServers[key].port = req.query.port; shepherd.electrumServers[key].port = req.query.port;
break; break;
}
} }
}
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',
result: true, result: true,
}; };
res.end(JSON.stringify(successObj));
} else {
const errorObj = {
msg: 'error',
result: 'unauthorized access',
};
res.end(JSON.stringify(successObj)); res.end(JSON.stringify(errorObj));
}
}); });
shepherd.get('/electrum/servers/test', (req, res, next) => { shepherd.get('/electrum/servers/test', (req, res, next) => {
const ecl = new shepherd.electrumJSCore(req.query.port, req.query.address, 'tcp'); // tcp or tls if (shepherd.checkToken(req.query.token)) {
const ecl = new shepherd.electrumJSCore(req.query.port, req.query.address, 'tcp'); // tcp or tls
ecl.connect();
ecl.serverVersion() ecl.connect();
.then((serverData) => { ecl.serverVersion()
ecl.close(); .then((serverData) => {
shepherd.log('serverData', true); ecl.close();
shepherd.log(serverData, true); shepherd.log('serverData', true);
shepherd.log(serverData, true);
if (serverData &&
typeof serverData === 'string' && if (serverData &&
serverData.indexOf('Electrum') > -1) { typeof serverData === 'string' &&
const successObj = { serverData.indexOf('Electrum') > -1) {
msg: 'success', const successObj = {
result: true, msg: 'success',
}; result: true,
};
res.end(JSON.stringify(successObj));
} else { res.end(JSON.stringify(successObj));
const successObj = { } else {
msg: 'error', const successObj = {
result: false, msg: 'error',
}; result: false,
};
res.end(JSON.stringify(successObj));
}
});
} else {
const errorObj = {
msg: 'error',
result: 'unauthorized access',
};
res.end(JSON.stringify(successObj)); res.end(JSON.stringify(errorObj));
} }
});
}); });
return shepherd; return shepherd;

573
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) => {
@ -12,135 +14,203 @@ module.exports = (shepherd) => {
return 0; return 0;
}); });
} }
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'] = {};
}
shepherd.get('/electrum/listtransactions', (req, res, next) => { if (!shepherd.electrumCache[network].tx[txid]) {
const network = req.query.network || shepherd.findNetworkObj(req.query.coin); shepherd.log(`electrum raw input tx ${txid}`, true);
const ecl = new shepherd.electrumJSCore(shepherd.electrumServers[network].port, shepherd.electrumServers[network].address, shepherd.electrumServers[network].proto); // tcp or tls
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.log('electrum listtransactions ==>', true); 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 (!req.query.full) { if (!shepherd.electrumCache[network].blockHeader[height]) {
ecl.connect(); shepherd.log(`electrum raw block ${height}`, true);
ecl.blockchainAddressGetHistory(req.query.address)
.then((json) => { ecl.blockchainBlockGetHeader(height)
ecl.close(); .then((_rawtxJSON) => {
shepherd.log(json, true); shepherd.electrumCache[network].blockHeader[height] = _rawtxJSON;
resolve(_rawtxJSON);
});
} else {
shepherd.log(`electrum cached raw block ${height}`, true);
resolve(shepherd.electrumCache[network].blockHeader[height]);
}
});
}
json = shepherd.sortTransactions(json); shepherd.get('/electrum/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 successObj = { shepherd.log('electrum listtransactions ==>', true);
msg: 'success',
result: json,
};
res.end(JSON.stringify(successObj)); if (!req.query.full) {
}); ecl.connect();
} else { ecl.blockchainAddressGetHistory(req.query.address)
// !expensive call! .then((json) => {
// TODO: limit e.g. 1-10, 10-20 etc ecl.close();
const MAX_TX = req.query.maxlength || 10; shepherd.log(json, true);
ecl.connect();
json = shepherd.sortTransactions(json);
const successObj = {
msg: 'success',
result: json,
};
ecl.blockchainNumblocksSubscribe() res.end(JSON.stringify(successObj));
.then((currentHeight) => { });
if (currentHeight && } else {
Number(currentHeight) > 0) { // !expensive call!
ecl.blockchainAddressGetHistory(req.query.address) // TODO: limit e.g. 1-10, 10-20 etc
.then((json) => { const MAX_TX = req.query.maxlength || 10;
if (json && ecl.connect();
json.length) {
json = shepherd.sortTransactions(json); ecl.blockchainNumblocksSubscribe()
json = json.length > MAX_TX ? json.slice(0, MAX_TX) : json; .then((currentHeight) => {
let _rawtx = []; if (currentHeight &&
Number(currentHeight) > 0) {
shepherd.log(json.length, true); ecl.blockchainAddressGetHistory(req.query.address)
.then((json) => {
shepherd.Promise.all(json.map((transaction, index) => { if (json &&
return new shepherd.Promise((resolve, reject) => { json.length) {
ecl.blockchainBlockGetHeader(transaction.height) let _rawtx = [];
json = shepherd.sortTransactions(json);
json = json.length > MAX_TX ? json.slice(0, MAX_TX) : json;
shepherd.log(json.length, true);
let index = 0;
async.eachOfSeries(json, (transaction, ind, callback) => {
shepherd.getBlockHeader(transaction.height, network, ecl)
.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); const _parsedTx = {
network: decodedTx.network,
if (decodedVinVout) { format: decodedTx.format,
shepherd.log(decodedVinVout.outputs[_decodedInput.n], true); inputs: txInputs,
txInputs.push(decodedVinVout.outputs[_decodedInput.n]); outputs: decodedTx.outputs,
_resolve(true); height: transaction.height,
} else { timestamp: Number(transaction.height) === 0 ? Math.floor(Date.now() / 1000) : blockInfo.timestamp,
_resolve(true); confirmations: Number(transaction.height) === 0 ? 0 : currentHeight - transaction.height,
} };
});
} else { const formattedTx = shepherd.parseTransactionAddresses(_parsedTx, req.query.address, network);
_resolve(true);
if (formattedTx.type) {
formattedTx.height = transaction.height;
formattedTx.blocktime = blockInfo.timestamp;
formattedTx.timereceived = blockInfo.timereceived;
formattedTx.hex = _rawtxJSON;
formattedTx.inputs = decodedTx.inputs;
formattedTx.outputs = decodedTx.outputs;
formattedTx.locktime = decodedTx.format.locktime;
_rawtx.push(formattedTx);
} else {
formattedTx[0].height = transaction.height;
formattedTx[0].blocktime = blockInfo.timestamp;
formattedTx[0].timereceived = blockInfo.timereceived;
formattedTx[0].hex = _rawtxJSON;
formattedTx[0].inputs = decodedTx.inputs;
formattedTx[0].outputs = decodedTx.outputs;
formattedTx[0].locktime = decodedTx.format.locktime;
formattedTx[1].height = transaction.height;
formattedTx[1].blocktime = blockInfo.timestamp;
formattedTx[1].timereceived = blockInfo.timereceived;
formattedTx[1].hex = _rawtxJSON;
formattedTx[1].inputs = decodedTx.inputs;
formattedTx[1].outputs = decodedTx.outputs;
formattedTx[1].locktime = decodedTx.format.locktime;
_rawtx.push(formattedTx[0]);
_rawtx.push(formattedTx[1]);
}
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();
})) }
.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,
confirmations: Number(transaction.height) === 0 ? 0 : currentHeight - transaction.height,
};
const formattedTx = shepherd.parseTransactionAddresses(_parsedTx, req.query.address, network); if (_decodedInput.txid !== '0000000000000000000000000000000000000000000000000000000000000000') {
shepherd.getTransaction(_decodedInput.txid, network, ecl)
if (formattedTx.type) { .then((rawInput) => {
formattedTx.height = transaction.height; const decodedVinVout = shepherd.electrumJSTxDecoder(rawInput, network, _network);
formattedTx.blocktime = blockInfo.timestamp;
formattedTx.timereceived = blockInfo.timereceived; if (decodedVinVout) {
formattedTx.hex = _rawtxJSON; shepherd.log(decodedVinVout.outputs[_decodedInput.n], true);
formattedTx.inputs = decodedTx.inputs; txInputs.push(decodedVinVout.outputs[_decodedInput.n]);
formattedTx.outputs = decodedTx.outputs; }
formattedTx.locktime = decodedTx.format.locktime; checkLoop();
_rawtx.push(formattedTx); });
} else { } else {
formattedTx[0].height = transaction.height; checkLoop();
formattedTx[0].blocktime = blockInfo.timestamp;
formattedTx[0].timereceived = blockInfo.timereceived;
formattedTx[0].hex = _rawtxJSON;
formattedTx[0].inputs = decodedTx.inputs;
formattedTx[0].outputs = decodedTx.outputs;
formattedTx[0].locktime = decodedTx.format.locktime;
formattedTx[1].height = transaction.height;
formattedTx[1].blocktime = blockInfo.timestamp;
formattedTx[1].timereceived = blockInfo.timereceived;
formattedTx[1].hex = _rawtxJSON;
formattedTx[1].inputs = decodedTx.inputs;
formattedTx[1].outputs = decodedTx.outputs;
formattedTx[1].locktime = decodedTx.format.locktime;
_rawtx.push(formattedTx[0]);
_rawtx.push(formattedTx[1]);
} }
resolve(true);
}); });
} else { } 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,67 +253,89 @@ 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 {
.then(promiseResult => {
ecl.close(); ecl.close();
const successObj = { const successObj = {
msg: 'success', msg: 'success',
result: _rawtx, result: [],
}; };
res.end(JSON.stringify(successObj)); res.end(JSON.stringify(successObj));
}); }
} else { });
const successObj = { } else {
msg: 'success', const successObj = {
result: [], msg: 'error',
}; result: 'cant get current height',
};
res.end(JSON.stringify(successObj));
} res.end(JSON.stringify(successObj));
}); }
} else { });
const successObj = { }
msg: 'error', } else {
result: 'cant get current height', const errorObj = {
}; msg: 'error',
result: 'unauthorized access',
};
res.end(JSON.stringify(successObj)); res.end(JSON.stringify(errorObj));
}
});
} }
}); });
shepherd.get('/electrum/gettransaction', (req, res, next) => { shepherd.get('/electrum/gettransaction', (req, res, next) => {
const network = req.query.network || shepherd.findNetworkObj(req.query.coin); if (shepherd.checkToken(req.query.token)) {
const ecl = new shepherd.electrumJSCore(shepherd.electrumServers[network].port, shepherd.electrumServers[network].address, shepherd.electrumServers[network].proto); // tcp or tls const network = req.query.network || shepherd.findNetworkObj(req.query.coin);
const ecl = new shepherd.electrumJSCore(shepherd.electrumServers[network].port, shepherd.electrumServers[network].address, shepherd.electrumServers[network].proto); // tcp or tls
shepherd.log('electrum gettransaction =>', true); shepherd.log('electrum gettransaction =>', true);
ecl.connect(); ecl.connect();
ecl.blockchainTransactionGet(req.query.txid) ecl.blockchainTransactionGet(req.query.txid)
.then((json) => { .then((json) => {
ecl.close(); ecl.close();
shepherd.log(json, true); shepherd.log(json, true);
const successObj = {
msg: 'success',
result: json,
};
const successObj = { res.end(JSON.stringify(successObj));
msg: 'success', });
result: json, } else {
const errorObj = {
msg: 'error',
result: 'unauthorized access',
}; };
res.end(JSON.stringify(successObj)); 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,39 +382,99 @@ 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
result = [{ // reorder since tx sort by default is from newest to oldest
type: 'sent',
amount: Number(_sum.inputs.toFixed(8)),
address: targetAddress,
timestamp: tx.timestamp,
txid: tx.format.txid,
confirmations: tx.confirmations,
}, {
type: 'received',
amount: Number(_sum.outputs.toFixed(8)),
address: targetAddress,
timestamp: tx.timestamp,
txid: tx.format.txid,
confirmations: tx.confirmations,
}];
if (network === 'komodo') { // calc claimed interest amount // send to self
const vinVoutDiff = _total.inputs - _total.outputs; 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) { if (vinVoutDiff < 0) {
result[1].interest = Number(vinVoutDiff.toFixed(8)); result.interest = Number(vinVoutDiff.toFixed(8));
}
}
} else {
result = [{ // reorder since tx sort by default is from newest to oldest
type: 'sent',
amount: Number(_sum.inputs.toFixed(8)),
address: _addresses.outputs[0],
timestamp: tx.timestamp,
txid: tx.format.txid,
confirmations: tx.confirmations,
from: _addresses.inputs,
to: _addresses.outputs,
}, {
type: 'received',
amount: Number(_sum.outputs.toFixed(8)),
address: targetAddress,
timestamp: tx.timestamp,
txid: tx.format.txid,
confirmations: tx.confirmations,
from: _addresses.inputs,
to: _addresses.outputs,
}];
if (network === 'komodo') { // calc claimed interest amount
const vinVoutDiff = _total.inputs - _total.outputs;
if (vinVoutDiff < 0) {
result[1].interest = Number(vinVoutDiff.toFixed(8));
}
} }
} }
} else if (_sum.inputs === 0 && _sum.outputs > 0) { } else if (_sum.inputs === 0 && _sum.outputs > 0) {
@ -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,51 +518,63 @@ module.exports = (shepherd) => {
} }
shepherd.get('/electrum/decoderawtx', (req, res, next) => { shepherd.get('/electrum/decoderawtx', (req, res, next) => {
const _network = shepherd.getNetworkData(req.query.network); if (shepherd.checkToken(req.query.token)) {
const _rawtx = req.query.rawtx; const _network = shepherd.getNetworkData(req.query.network);
// const _rawtx = '0100000001dd6d064f5665f8454293ecaa9dbb55accf4f7e443d35f3b5ab7760f54b6c15fe000000006a473044022056355585a4a501ec9afc96aa5df124cf29ad3ac6454b47cd07cd7d89ec95ec2b022074c4604ee349d30e5336f210598e4dc576bf16ebeb67eeac3f4e82f56e930fee012103b90ba01af308757054e0484bb578765d5df59c4a57adbb94e2419df5e7232a63feffffff0289fc923b000000001976a91424af38fcb13bbc171b0b42bb017244a53b6bb2fa88ac20a10700000000001976a9142f4c0f91fc06ac228c120aee41741d0d3909683288ac49258b58'; const _rawtx = req.query.rawtx;
const decodedTx = shepherd.electrumJSTxDecoder(_rawtx, _network); //const _rawtx = '010000006f2c395a02d81487fc7f9d1be3ea900316730133c044af70cd76d21e988e71de0e9e85918f010000006a47304402202097acd391e1d0eaaf91844bd596e918fb71320e3e0c51554acb71a39e4ee98b0220548fd61d4ae77a08d70b01bf5340983a1ba63f6b71ad71d478af77011f96fd510121031ffc010d8abc4180b4c1a13962bf9153a78082e7f2ac18f7d14cb6a6634ca218feffffff2b31f6c9a7916f7cf128cae94b3fc10e4c74ca3a740e1a7a6fd6624e4e9a5c8b010000006a473044022063f014c5fbaa7614732e0ae486179a854215fc32c02230e13f69b7e81fa000e50220236a2ba6373b1854aafc59c5391ab7505062067f3d293c016cbb5d252b35a56a012102f307f17d282fc0eabf99227c2e0f3122ae9ecd7da0de099f0c6007d4c941b57bfeffffff021b797ad7120000001976a914c7a7142d743b3e6eebe76923f43bae477d3ce31a88acff086d66000000001976a91463800ff36b9c52b2ffe5564af1c2a38df4f0126788ac16381d00';
const decodedTx = shepherd.electrumJSTxDecoder(_rawtx, req.query.network, _network);
shepherd.log('electrum decoderawtx input tx ==>', true);
if (req.query.parseonly ||
decodedTx.inputs[0].txid === '0000000000000000000000000000000000000000000000000000000000000000') {
const successObj = {
msg: 'success',
result: {
network: decodedTx.network,
format: decodedTx.format,
inputs: decodedTx.inputs,
outputs: decodedTx.outputs,
},
};
shepherd.log(successObj.result, true);
res.end(JSON.stringify(successObj));
} else {
const ecl = new shepherd.electrumJSCore(shepherd.electrumServers[req.query.network].port, shepherd.electrumServers[req.query.network].address, shepherd.electrumServers[req.query.network].proto); // tcp or tls
ecl.connect(); shepherd.log('electrum decoderawtx input tx ==>', true);
ecl.blockchainTransactionGet(decodedTx.inputs[0].txid)
.then((json) => {
ecl.close();
shepherd.log(json, true);
const decodedVin = shepherd.electrumJSTxDecoder(json, _network);
if (req.query.parseonly ||
decodedTx.inputs[0].txid === '0000000000000000000000000000000000000000000000000000000000000000') {
const successObj = { const successObj = {
msg: 'success', msg: 'success',
result: { result: {
network: decodedTx.network, network: decodedTx.network,
format: decodedTx.format, format: decodedTx.format,
inputs: decodedVin.outputs[decodedTx.inputs[0].n], inputs: decodedTx.inputs,
outputs: decodedTx.outputs, outputs: decodedTx.outputs,
}, },
}; };
shepherd.log(successObj.result, true);
res.end(JSON.stringify(successObj)); res.end(JSON.stringify(successObj));
}); } else {
const ecl = new shepherd.electrumJSCore(shepherd.electrumServers[req.query.network].port, shepherd.electrumServers[req.query.network].address, shepherd.electrumServers[req.query.network].proto); // tcp or tls
shepherd.log(decodedTx.inputs[0]);
shepherd.log(decodedTx.inputs[0].txid);
ecl.connect();
ecl.blockchainTransactionGet(decodedTx.inputs[0].txid)
.then((json) => {
ecl.close();
shepherd.log(json, true);
const decodedVin = shepherd.electrumJSTxDecoder(json, req.query.network, _network);
const successObj = {
msg: 'success',
result: {
network: decodedTx.network,
format: decodedTx.format,
inputs: decodedVin.outputs[decodedTx.inputs[0].n],
outputs: decodedTx.outputs,
},
};
res.end(JSON.stringify(successObj));
});
}
} else {
const errorObj = {
msg: 'error',
result: 'unauthorized access',
};
res.end(JSON.stringify(errorObj));
} }
}); });

59
routes/shepherd/kickstart.js

@ -7,37 +7,46 @@ module.exports = (shepherd) => {
* params: coin, type * params: coin, type
*/ */
shepherd.get('/kick', (req, res, next) => { shepherd.get('/kick', (req, res, next) => {
const _coin = req.query.coin; if (shepherd.checkToken(req.query.token)) {
const _keepWallet = req.query.keepwallet; const _coin = req.query.coin;
const _keepWallet = req.query.keepwallet;
if (!_coin) {
const errorObj = {
msg: 'error',
result: 'no coin name provided',
};
res.end(JSON.stringify(errorObj));
} else {
const _location = path.join(_coin === 'KMD' ? shepherd.komodoDir : `${shepherd.komodoDir}/${_coin}`);
if (fs.existsSync(_location)) {
const items = fs.readdirSync(_location);
for (let i = 0; i < items.length; i++) {
if (items[i].indexOf('wallet.dat') === -1) {
fs.removeSync(`${_location}/${items[i]}`);
} else if (!_keepWallet) {
fs.removeSync(`${_location}/${items[i]}`);
}
}
}
if (!_coin) { const successObj = {
msg: 'success',
result: `${_coin} native is kicked`,
};
res.end(JSON.stringify(successObj));
}
} else {
const errorObj = { const errorObj = {
msg: 'error', msg: 'error',
result: 'no coin name provided', result: 'unauthorized access',
}; };
res.end(JSON.stringify(errorObj)); res.end(JSON.stringify(errorObj));
} else {
const _location = path.join(_coin === 'KMD' ? shepherd.komodoDir : `${shepherd.komodoDir}/${_coin}`);
if (fs.existsSync(_location)) {
const items = fs.readdirSync(_location);
for (let i = 0; i < items.length; i++) {
if (items[i].indexOf('wallet.dat') === -1) {
fs.removeSync(`${_location}/${items[i]}`);
} else if (!_keepWallet) {
fs.removeSync(`${_location}/${items[i]}`);
}
}
}
const successObj = {
msg: 'success',
result: `${_coin} native is kicked`,
};
res.end(JSON.stringify(successObj));
} }
}); });

127
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) => {
const successObj = { if (shepherd.checkToken(req.query.token)) {
msg: 'success', const successObj = {
result: req.query.spv && req.query.spv === 'true' ? shepherd.appRuntimeSPVLog : shepherd.appRuntimeLog, msg: 'success',
}; result: req.query.spv && req.query.spv === 'true' ? shepherd.appRuntimeSPVLog : shepherd.appRuntimeLog,
};
res.end(JSON.stringify(successObj));
} else {
const errorObj = {
msg: 'error',
result: 'unauthorized access',
};
res.end(JSON.stringify(successObj)); res.end(JSON.stringify(errorObj));
}
}); });
shepherd.getAppRuntimeLog = () => { shepherd.getAppRuntimeLog = () => {
@ -59,37 +68,46 @@ module.exports = (shepherd) => {
* params: payload * params: payload
*/ */
shepherd.post('/guilog', (req, res, next) => { shepherd.post('/guilog', (req, res, next) => {
const logLocation = `${shepherd.agamaDir}/shepherd`; if (shepherd.checkToken(req.body.token)) {
const logLocation = `${shepherd.agamaDir}/shepherd`;
if (!shepherd.guiLog[shepherd.appSessionHash]) { if (!shepherd.guiLog[shepherd.appSessionHash]) {
shepherd.guiLog[shepherd.appSessionHash] = {}; shepherd.guiLog[shepherd.appSessionHash] = {};
} }
if (shepherd.guiLog[shepherd.appSessionHash][req.body.timestamp]) {
shepherd.guiLog[shepherd.appSessionHash][req.body.timestamp].status = req.body.status;
shepherd.guiLog[shepherd.appSessionHash][req.body.timestamp].response = req.body.response;
} else {
shepherd.guiLog[shepherd.appSessionHash][req.body.timestamp] = {
function: req.body.function,
type: req.body.type,
url: req.body.url,
payload: req.body.payload,
status: req.body.status,
};
}
shepherd.fs.writeFile(`${logLocation}/agamalog.json`, JSON.stringify(shepherd.guiLog), (err) => { if (shepherd.guiLog[shepherd.appSessionHash][req.body.timestamp]) {
if (err) { shepherd.guiLog[shepherd.appSessionHash][req.body.timestamp].status = req.body.status;
shepherd.writeLog('error writing gui log file'); shepherd.guiLog[shepherd.appSessionHash][req.body.timestamp].response = req.body.response;
} else {
shepherd.guiLog[shepherd.appSessionHash][req.body.timestamp] = {
function: req.body.function,
type: req.body.type,
url: req.body.url,
payload: req.body.payload,
status: req.body.status,
};
} }
const returnObj = { shepherd.fs.writeFile(`${logLocation}/agamalog.json`, JSON.stringify(shepherd.guiLog), (err) => {
msg: 'success', if (err) {
result: 'gui log entry is added', shepherd.writeLog('error writing gui log file');
}
const returnObj = {
msg: 'success',
result: 'gui log entry is added',
};
res.end(JSON.stringify(returnObj));
});
} else {
const errorObj = {
msg: 'error',
result: 'unauthorized access',
}; };
res.end(JSON.stringify(returnObj)); res.end(JSON.stringify(errorObj));
}); }
}); });
/* /*
@ -97,30 +115,39 @@ module.exports = (shepherd) => {
* params: type * params: type
*/ */
shepherd.get('/getlog', (req, res, next) => { shepherd.get('/getlog', (req, res, next) => {
const logExt = req.query.type === 'txt' ? 'txt' : 'json'; if (shepherd.checkToken(req.query.token)) {
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}`)) {
shepherd.fs.readFile(`${shepherd.agamaDir}/shepherd/agamalog.${logExt}`, 'utf8', (err, data) => { shepherd.fs.readFile(`${shepherd.agamaDir}/shepherd/agamalog.${logExt}`, 'utf8', (err, data) => {
if (err) { if (err) {
const errorObj = { const errorObj = {
msg: 'error', msg: 'error',
result: err, result: err,
}; };
res.end(JSON.stringify(errorObj)); res.end(JSON.stringify(errorObj));
} else { } else {
const successObj = { const successObj = {
msg: 'success', msg: 'success',
result: data ? JSON.parse(data) : '', result: data ? JSON.parse(data) : '',
}; };
res.end(JSON.stringify(successObj)); res.end(JSON.stringify(successObj));
} }
}); });
} else {
const errorObj = {
msg: 'error',
result: `agama.${logExt} doesnt exist`,
};
res.end(JSON.stringify(errorObj));
}
} else { } else {
const errorObj = { const errorObj = {
msg: 'error', msg: 'error',
result: `agama.${logExt} doesnt exist`, result: 'unauthorized access',
}; };
res.end(JSON.stringify(errorObj)); res.end(JSON.stringify(errorObj));

209
routes/shepherd/pin.js

@ -7,93 +7,111 @@ module.exports = (shepherd) => {
* params: none * params: none
*/ */
shepherd.post('/encryptkey', (req, res, next) => { shepherd.post('/encryptkey', (req, res, next) => {
if (req.body.key && if (shepherd.checkToken(req.body.token)) {
req.body.string && if (req.body.key &&
req.body.pubkey) { req.body.string &&
const encryptedString = aes256.encrypt(req.body.key, req.body.string); req.body.pubkey) {
const encryptedString = aes256.encrypt(req.body.key, req.body.string);
// test pin security
// - at least 1 char in upper case // test pin security
// - at least 1 digit // - at least 1 char in upper case
// - at least one special character // - at least 1 digit
// - min length 8 // - at least one special character
// - min length 8
const _pin = req.body.key;
const _pinTest = _pin.match('^(?=.*[A-Z])(?=.*[^<>{}\"/|;:.,~!?@#$%^=&*\\]\\\\()\\[_+]*$)(?=.*[0-9])(?=.*[a-z]).{8}$'); const _pin = req.body.key;
const _pinTest = _pin.match('^(?=.*[A-Z])(?=.*[^<>{}\"/|;:.,~!?@#$%^=&*\\]\\\\()\\[_+]*$)(?=.*[0-9])(?=.*[a-z]).{8}$');
fs.writeFile(`${shepherd.agamaDir}/shepherd/pin/${req.body.pubkey}.pin`, encryptedString, (err) => {
if (err) { fs.writeFile(`${shepherd.agamaDir}/shepherd/pin/${req.body.pubkey}.pin`, encryptedString, (err) => {
shepherd.log('error writing pin file'); if (err) {
} shepherd.log('error writing pin file');
}
const returnObj = {
msg: 'success',
result: encryptedString,
};
const returnObj = { res.end(JSON.stringify(returnObj));
msg: 'success', });
result: encryptedString, } else {
const _paramsList = [
'key',
'string',
'pubkey'
];
let errorObj = {
msg: 'error',
result: '',
}; };
let _errorParamsList = [];
for (let i = 0; i < _paramsList.length; i++) {
if (!req.query[_paramsList[i]]) {
_errorParamsList.push(_paramsList[i]);
}
}
res.end(JSON.stringify(returnObj)); errorObj.result = `missing param ${_errorParamsList.join(', ')}`;
}); res.end(JSON.stringify(errorObj));
}
} else { } else {
let errorObj = { const errorObj = {
msg: 'error', msg: 'error',
result: '', result: 'unauthorized access',
}; };
const _paramsList = [
'key',
'string',
'pubkey'
];
let _errorParamsList = [];
for (let i = 0; i < _paramsList.length; i++) {
if (!req.query[_paramsList[i]]) {
_errorParamsList.push(_paramsList[i]);
}
}
errorObj.result = `missing param ${_errorParamsList.join(', ')}`;
res.end(JSON.stringify(errorObj)); res.end(JSON.stringify(errorObj));
} }
}); });
shepherd.post('/decryptkey', (req, res, next) => { shepherd.post('/decryptkey', (req, res, next) => {
if (req.body.key && if (shepherd.checkToken(req.body.token)) {
req.body.pubkey) { if (req.body.key &&
if (fs.existsSync(`${shepherd.agamaDir}/shepherd/pin/${req.body.pubkey}.pin`)) { req.body.pubkey) {
fs.readFile(`${shepherd.agamaDir}/shepherd/pin/${req.body.pubkey}.pin`, 'utf8', (err, data) => { if (fs.existsSync(`${shepherd.agamaDir}/shepherd/pin/${req.body.pubkey}.pin`)) {
if (err) { fs.readFile(`${shepherd.agamaDir}/shepherd/pin/${req.body.pubkey}.pin`, 'utf8', (err, data) => {
const errorObj = { if (err) {
msg: 'error', const errorObj = {
result: err,
};
res.end(JSON.stringify(errorObj));
} else {
const encryptedKey = aes256.decrypt(req.body.key, data);
// test if stored encrypted passphrase is decrypted correctly
// if not then the key is wrong
const _regexTest = encryptedKey.match(/^[0-9a-zA-Z ]+$/g);
let returnObj;
if (!_regexTest) {
returnObj = {
msg: 'error', msg: 'error',
result: 'wrong key', result: err,
}; };
res.end(JSON.stringify(errorObj));
} else { } else {
returnObj = { const encryptedKey = aes256.decrypt(req.body.key, data);
msg: 'success', // test if stored encrypted passphrase is decrypted correctly
result: encryptedKey, // if not then the key is wrong
}; const _regexTest = encryptedKey.match(/^[0-9a-zA-Z ]+$/g);
let returnObj;
if (!_regexTest) {
returnObj = {
msg: 'error',
result: 'wrong key',
};
} else {
returnObj = {
msg: 'success',
result: encryptedKey,
};
}
res.end(JSON.stringify(returnObj));
} }
});
} else {
const errorObj = {
msg: 'error',
result: `file ${req.query.pubkey}.pin doesnt exist`,
};
res.end(JSON.stringify(returnObj)); res.end(JSON.stringify(errorObj));
} }
});
} else { } else {
const errorObj = { const errorObj = {
msg: 'error', msg: 'error',
result: `file ${req.query.pubkey}.pin doesnt exist`, result: 'missing key or pubkey param',
}; };
res.end(JSON.stringify(errorObj)); res.end(JSON.stringify(errorObj));
@ -101,7 +119,7 @@ module.exports = (shepherd) => {
} else { } else {
const errorObj = { const errorObj = {
msg: 'error', msg: 'error',
result: 'missing key or pubkey param', result: 'unauthorized access',
}; };
res.end(JSON.stringify(errorObj)); res.end(JSON.stringify(errorObj));
@ -109,36 +127,45 @@ module.exports = (shepherd) => {
}); });
shepherd.get('/getpinlist', (req, res, next) => { shepherd.get('/getpinlist', (req, res, next) => {
if (fs.existsSync(`${shepherd.agamaDir}/shepherd/pin`)) { if (shepherd.checkToken(req.body.token)) {
fs.readdir(`${shepherd.agamaDir}/shepherd/pin`, (err, items) => { if (fs.existsSync(`${shepherd.agamaDir}/shepherd/pin`)) {
let _pins = []; fs.readdir(`${shepherd.agamaDir}/shepherd/pin`, (err, items) => {
let _pins = [];
for (let i = 0; i < items.length; i++) {
if (items[i].substr(items[i].length - 4, 4) === '.pin') { for (let i = 0; i < items.length; i++) {
_pins.push(items[i].substr(0, items[i].length - 4)); if (items[i].substr(items[i].length - 4, 4) === '.pin') {
_pins.push(items[i].substr(0, items[i].length - 4));
}
} }
}
if (!items.length) { if (!items.length) {
const errorObj = { const errorObj = {
msg: 'error', msg: 'error',
result: 'no pins', result: 'no pins',
}; };
res.end(JSON.stringify(errorObj)); res.end(JSON.stringify(errorObj));
} else { } else {
const successObj = { const successObj = {
msg: 'success', msg: 'success',
result: _pins, result: _pins,
}; };
res.end(JSON.stringify(successObj)); res.end(JSON.stringify(successObj));
} }
}); });
} else {
const errorObj = {
msg: 'error',
result: 'pin folder doesnt exist',
};
res.end(JSON.stringify(errorObj));
}
} else { } else {
const errorObj = { const errorObj = {
msg: 'error', msg: 'error',
result: 'pin folder doesnt exist', result: 'unauthorized access',
}; };
res.end(JSON.stringify(errorObj)); res.end(JSON.stringify(errorObj));

132
routes/shepherd/quitDaemon.js

@ -82,41 +82,32 @@ module.exports = (shepherd) => {
} }
shepherd.post('/coind/stop', (req, res) => { shepherd.post('/coind/stop', (req, res) => {
const _chain = req.body.chain; if (shepherd.checkToken(req.body.token)) {
let _coindQuitCmd = shepherd.komodocliBin; const _chain = req.body.chain;
let _arg = []; let _coindQuitCmd = shepherd.komodocliBin;
let _arg = [];
if (_chain) { if (_chain) {
_arg.push(`-ac_name=${_chain}`); _arg.push(`-ac_name=${_chain}`);
if (shepherd.appConfig.dataDir.length) { if (shepherd.appConfig.dataDir.length) {
_arg.push(`-datadir=${shepherd.appConfig.dataDir + (_chain ? '/' + _chain : '')}`); _arg.push(`-datadir=${shepherd.appConfig.dataDir + (_chain ? '/' + _chain : '')}`);
}
} else if (!_chain && shepherd.appConfig.dataDir.length) {
_arg.push(`-datadir=${shepherd.appConfig.dataDir}`);
} }
} else if (!_chain && shepherd.appConfig.dataDir.length) {
_arg.push(`-datadir=${shepherd.appConfig.dataDir}`);
}
_arg.push('stop');
execFile(`${_coindQuitCmd}`, _arg, (error, stdout, stderr) => {
shepherd.log(`stdout: ${stdout}`);
shepherd.log(`stderr: ${stderr}`);
shepherd.log(`send stop sig to ${_chain ? _chain : 'komodo'}`);
if (stdout.indexOf('EOF reached') > -1 ||
stderr.indexOf('EOF reached') > -1 ||
(error && error.toString().indexOf('Command failed') > -1 && !stderr) || // win "special snowflake" case
stdout.indexOf('connect to server: unknown (code -1)') > -1 ||
stderr.indexOf('connect to server: unknown (code -1)') > -1) {
delete shepherd.coindInstanceRegistry[_chain ? _chain : 'komodod'];
const obj = { _arg.push('stop');
msg: 'success', execFile(`${_coindQuitCmd}`, _arg, (error, stdout, stderr) => {
result: 'result', shepherd.log(`stdout: ${stdout}`);
}; shepherd.log(`stderr: ${stderr}`);
shepherd.log(`send stop sig to ${_chain ? _chain : 'komodo'}`);
res.end(JSON.stringify(obj));
} else { if (stdout.indexOf('EOF reached') > -1 ||
if (stdout.indexOf('Komodo server stopping') > -1) { stderr.indexOf('EOF reached') > -1 ||
(error && error.toString().indexOf('Command failed') > -1 && !stderr) || // win "special snowflake" case
stdout.indexOf('connect to server: unknown (code -1)') > -1 ||
stderr.indexOf('connect to server: unknown (code -1)') > -1) {
delete shepherd.coindInstanceRegistry[_chain ? _chain : 'komodod']; delete shepherd.coindInstanceRegistry[_chain ? _chain : 'komodod'];
const obj = { const obj = {
@ -126,43 +117,70 @@ module.exports = (shepherd) => {
res.end(JSON.stringify(obj)); res.end(JSON.stringify(obj));
} else { } else {
const obj = { if (stdout.indexOf('Komodo server stopping') > -1) {
msg: 'error', delete shepherd.coindInstanceRegistry[_chain ? _chain : 'komodod'];
result: 'result',
}; const obj = {
msg: 'success',
res.end(JSON.stringify(obj)); result: 'result',
};
res.end(JSON.stringify(obj));
} else {
const obj = {
msg: 'error',
result: 'result',
};
res.end(JSON.stringify(obj));
}
} }
} });
}); } 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) => {
const _chain = req.body.chain; if (shepherd.checkToken(req.body.token)) {
const _chain = req.body.chain;
if (req.body.mode === 'native') { if (req.body.mode === 'native') {
delete shepherd.coindInstanceRegistry[_chain ? _chain : 'komodod']; delete shepherd.coindInstanceRegistry[_chain ? _chain : 'komodod'];
const obj = { const obj = {
msg: 'success', msg: 'success',
result: 'result', result: 'result',
}; };
res.end(JSON.stringify(obj)); res.end(JSON.stringify(obj));
} else { } else {
delete shepherd.electrumCoins[_chain === 'komodo' ? 'KMD' : _chain]; delete shepherd.electrumCoins[_chain === 'komodo' ? 'KMD' : _chain];
if (Object.keys(shepherd.electrumCoins).length - 1 === 0) { if (Object.keys(shepherd.electrumCoins).length - 1 === 0) {
shepherd.electrumCoins.auth = false; shepherd.electrumCoins.auth = false;
shepherd.electrumKeys = {}; shepherd.electrumKeys = {};
} }
const obj = {
msg: 'success',
result: 'result',
};
const obj = { res.end(JSON.stringify(obj));
msg: 'success', }
result: 'result', } else {
const errorObj = {
msg: 'error',
result: 'unauthorized access',
}; };
res.end(JSON.stringify(obj)); res.end(JSON.stringify(errorObj));
} }
}); });

373
routes/shepherd/rpc.js

@ -60,160 +60,279 @@ module.exports = (shepherd) => {
* params: payload * params: payload
*/ */
shepherd.post('/cli', (req, res, next) => { shepherd.post('/cli', (req, res, next) => {
if (!req.body.payload) { if (shepherd.checkToken(req.body.payload.token)) {
const errorObj = { if (!req.body.payload) {
msg: 'error', const errorObj = {
result: 'no payload provided', msg: 'error',
}; result: 'no payload provided',
};
res.end(JSON.stringify(errorObj));
} else if (!req.body.payload.cmd.match(/^[0-9a-zA-Z _\,\.\[\]"'/\\]+$/g)) {
const errorObj = {
msg: 'error',
result: 'wrong cli string format',
};
res.end(JSON.stringify(errorObj));
} else {
const _mode = req.body.payload.mode === 'passthru' ? 'passthru' : 'default';
const _chain = req.body.payload.chain === 'KMD' ? null : req.body.payload.chain;
let _params = req.body.payload.params ? ` ${req.body.payload.params}` : '';
let _cmd = req.body.payload.cmd;
res.end(JSON.stringify(errorObj)); if (!shepherd.rpcConf[_chain]) {
} else if (!req.body.payload.cmd.match(/^[0-9a-zA-Z _\,\.\[\]"'/\\]+$/g)) { shepherd.getConf(req.body.payload.chain === 'KMD' || !req.body.payload.chain && shepherd.kmdMainPassiveMode ? 'komodod' : req.body.payload.chain);
const errorObj = { }
msg: 'error',
result: 'wrong cli string format',
};
res.end(JSON.stringify(errorObj)); if (_mode === 'default') {
} else { if (req.body.payload.rpc2cli) {
const _mode = req.body.payload.mode === 'passthru' ? 'passthru' : 'default'; let _coindCliBin = shepherd.komodocliBin;
const _chain = req.body.payload.chain === 'KMD' ? null : req.body.payload.chain;
const _params = req.body.payload.params ? ` ${req.body.payload.params}` : '';
let _cmd = req.body.payload.cmd;
if (!shepherd.rpcConf[_chain]) { if (shepherd.nativeCoindList &&
shepherd.getConf(req.body.payload.chain === 'KMD' || !req.body.payload.chain && shepherd.kmdMainPassiveMode ? 'komodod' : req.body.payload.chain); _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 ""';
}
if (_mode === 'default') { let _arg = (_chain ? ' -ac_name=' + _chain : '') + ' ' + _cmd + (typeof _params === 'object' ? _params.join(' ') : _params);
if (_cmd === 'debug' &&
_chain !== 'CHIPS') {
if (shepherd.nativeCoindList[_chain.toLowerCase()]) {
const _osHome = os.platform === 'win32' ? process.env.APPDATA : process.env.HOME;
let coindDebugLogLocation;
if (_chain === 'CHIPS') { if (shepherd.appConfig.dataDir.length) {
coindDebugLogLocation = `${shepherd.chipsDir}/debug.log`; _arg = `${_arg} -datadir=${shepherd.appConfig.dataDir + (_chain ? '/' + key : '')}`;
} else {
coindDebugLogLocation = `${_osHome}/.${shepherd.nativeCoindList[_chain.toLowerCase()].bin.toLowerCase()}/debug.log`;
} }
shepherd.readDebugLog(coindDebugLogLocation, 1) shepherd.exec(`"${_coindCliBin}" ${_arg}`, (error, stdout, stderr) => {
.then((result) => { //shepherd.log(`stdout: ${stdout}`);
const _obj = { //shepherd.log(`stderr: ${stderr}`);
msg: 'success',
result: result,
};
// shepherd.log('bitcoinrpc debug ====>'); if (error !== null) {
// console.log(result); shepherd.log(`exec error: ${error}`);
}
res.end(JSON.stringify(_obj)); let responseObj;
}, (result) => {
const _obj = { if (stderr) {
error: result, let _res;
result: 'error', 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(_obj)); res.end(JSON.stringify(responseObj));
// shepherd.killRogueProcess('komodo-cli');
}); });
} else { } else {
res.end({ if (_cmd === 'debug' &&
error: 'bitcoinrpc debug error', _chain !== 'CHIPS') {
result: 'error', if (shepherd.nativeCoindList[_chain.toLowerCase()]) {
}); const _osHome = os.platform === 'win32' ? process.env.APPDATA : process.env.HOME;
// console.log('bitcoinrpc debug error'); let coindDebugLogLocation;
}
} else { if (_chain === 'CHIPS') {
if (_chain === 'CHIPS' && coindDebugLogLocation = `${shepherd.chipsDir}/debug.log`;
_cmd === 'debug') { } else {
_cmd = 'getblockchaininfo'; coindDebugLogLocation = `${_osHome}/.${shepherd.nativeCoindList[_chain.toLowerCase()].bin.toLowerCase()}/debug.log`;
} }
shepherd.readDebugLog(coindDebugLogLocation, 1)
.then((result) => {
const _obj = {
msg: 'success',
result: result,
};
// shepherd.log('bitcoinrpc debug ====>');
// console.log(result);
res.end(JSON.stringify(_obj));
}, (result) => {
const _obj = {
error: result,
result: 'error',
};
res.end(JSON.stringify(_obj));
});
} else {
res.end({
error: 'bitcoinrpc debug error',
result: 'error',
});
// console.log('bitcoinrpc debug error');
}
} else {
if (_chain === 'CHIPS' &&
_cmd === 'debug') {
_cmd = 'getblockchaininfo';
}
let _body = { let _body = {
agent: 'bitcoinrpc', agent: 'bitcoinrpc',
method: _cmd, method: _cmd,
}; };
if (req.body.payload.params) { if (req.body.payload.params) {
_body = { _body = {
agent: 'bitcoinrpc', agent: 'bitcoinrpc',
method: _cmd, method: _cmd,
params: req.body.payload.params === ' ' ? [''] : req.body.payload.params, params: req.body.payload.params === ' ' ? [''] : req.body.payload.params,
}; };
} }
if (req.body.payload.chain) { if (req.body.payload.chain) {
const options = { const options = {
url: `http://localhost:${shepherd.rpcConf[req.body.payload.chain].port}`, url: `http://localhost:${shepherd.rpcConf[req.body.payload.chain].port}`,
method: 'POST', method: 'POST',
auth: { auth: {
user: shepherd.rpcConf[req.body.payload.chain].user, user: shepherd.rpcConf[req.body.payload.chain].user,
pass: shepherd.rpcConf[req.body.payload.chain].pass, pass: shepherd.rpcConf[req.body.payload.chain].pass,
},
body: JSON.stringify(_body),
};
// send back body on both success and error
// this bit replicates iguana core's behaviour
shepherd.request(options, (error, response, body) => {
if (response &&
response.statusCode &&
response.statusCode === 200) {
res.end(body);
} else {
res.end(body ? body : JSON.stringify({
result: 'error',
error: {
code: -777,
message: `unable to call method ${_cmd} at port ${shepherd.rpcConf[req.body.payload.chain].port}`,
}, },
})); body: JSON.stringify(_body),
};
// send back body on both success and error
// this bit replicates iguana core's behaviour
shepherd.request(options, (error, response, body) => {
if (response &&
response.statusCode &&
response.statusCode === 200) {
res.end(body);
} else {
res.end(body ? body : JSON.stringify({
result: 'error',
error: {
code: -777,
message: `unable to call method ${_cmd} at port ${shepherd.rpcConf[req.body.payload.chain].port}`,
},
}));
}
});
} }
}); }
} }
} } else {
} else { let _coindCliBin = shepherd.komodocliBin;
let _coindCliBin = shepherd.komodocliBin;
if (shepherd.nativeCoindList && if (shepherd.nativeCoindList &&
_chain && _chain &&
shepherd.nativeCoindList[_chain.toLowerCase()]) { shepherd.nativeCoindList[_chain.toLowerCase()]) {
_coindCliBin = `${shepherd.coindRootDir}/${_chain.toLowerCase()}/${shepherd.nativeCoindList[_chain.toLowerCase()].bin.toLowerCase()}-cli`; _coindCliBin = `${shepherd.coindRootDir}/${_chain.toLowerCase()}/${shepherd.nativeCoindList[_chain.toLowerCase()].bin.toLowerCase()}-cli`;
} }
let _arg = (_chain ? ' -ac_name=' + _chain : '') + ' ' + _cmd + _params; let _arg = (_chain ? ' -ac_name=' + _chain : '') + ' ' + _cmd + _params;
if (shepherd.appConfig.dataDir.length) { if (shepherd.appConfig.dataDir.length) {
_arg = `${_arg} -datadir=${shepherd.appConfig.dataDir + (_chain ? '/' + key : '')}`; _arg = `${_arg} -datadir=${shepherd.appConfig.dataDir + (_chain ? '/' + key : '')}`;
} }
_arg = _arg.trim().split(' '); _arg = _arg.trim().split(' ');
shepherd.execFile(_coindCliBin, _arg, (error, stdout, stderr) => { shepherd.execFile(_coindCliBin, _arg, (error, stdout, stderr) => {
shepherd.log(`stdout: ${stdout}`); shepherd.log(`stdout: ${stdout}`);
shepherd.log(`stderr: ${stderr}`); shepherd.log(`stderr: ${stderr}`);
if (error !== null) { if (error !== null) {
shepherd.log(`exec error: ${error}`); shepherd.log(`exec error: ${error}`);
} }
let responseObj; let responseObj;
if (stderr) { if (stderr) {
responseObj = { responseObj = {
msg: 'error', msg: 'error',
result: stderr, result: stderr,
}; };
} else { } else {
responseObj = { responseObj = {
msg: 'success', msg: 'success',
result: stdout, result: stdout,
}; };
} }
res.end(JSON.stringify(responseObj)); res.end(JSON.stringify(responseObj));
shepherd.killRogueProcess('komodo-cli'); shepherd.killRogueProcess('komodo-cli');
}); });
}
} }
} else {
const errorObj = {
msg: 'error',
result: 'unauthorized access',
};
res.end(JSON.stringify(errorObj));
} }
}); });

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