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. 310
      main.js
  4. 22
      package.json
  5. 126
      private/kmdcli.js
  6. 36
      private/mainmenu.js
  7. 24
      routes/appConfig.js
  8. 289
      routes/electrumjs/electrumServers.js
  9. 173
      routes/electrumjs/electrumjs.networks.js
  10. 120
      routes/electrumjs/electrumjs.txdecoder-2bytes.js
  11. 123
      routes/electrumjs/electrumjs.txdecoder-pos.js
  12. 40
      routes/ports.js
  13. 12
      routes/shepherd.js
  14. 150
      routes/shepherd/addCoinShortcuts.js
  15. 19
      routes/shepherd/appInfo.js
  16. 18
      routes/shepherd/auth.js
  17. 239
      routes/shepherd/binsTestUtil.js
  18. 9
      routes/shepherd/coindWalletKeys.js
  19. 9
      routes/shepherd/coins.js
  20. 18
      routes/shepherd/coinsList.js
  21. 27
      routes/shepherd/config.js
  22. 136
      routes/shepherd/daemonControl.js
  23. 20
      routes/shepherd/dashboardUpdate.js
  24. 18
      routes/shepherd/debugLog.js
  25. 18
      routes/shepherd/downloadBins.js
  26. 29
      routes/shepherd/downloadPatch.js
  27. 9
      routes/shepherd/downloadZcparams.js
  28. 372
      routes/shepherd/elections.js
  29. 67
      routes/shepherd/electrum/auth.js
  30. 23
      routes/shepherd/electrum/balance.js
  31. 18
      routes/shepherd/electrum/block.js
  32. 47
      routes/shepherd/electrum/coins.js
  33. 500
      routes/shepherd/electrum/createtx-multi.js
  34. 86
      routes/shepherd/electrum/createtx-split.js
  35. 306
      routes/shepherd/electrum/createtx.js
  36. 9
      routes/shepherd/electrum/estimate.js
  37. 2
      routes/shepherd/electrum/interest.js
  38. 267
      routes/shepherd/electrum/keys.js
  39. 27
      routes/shepherd/electrum/listunspent.js
  40. 30
      routes/shepherd/electrum/merkle.js
  41. 81
      routes/shepherd/electrum/network.js
  42. 279
      routes/shepherd/electrum/transactions.js
  43. 9
      routes/shepherd/kickstart.js
  44. 27
      routes/shepherd/log.js
  45. 35
      routes/shepherd/pin.js
  46. 18
      routes/shepherd/quitDaemon.js
  47. 121
      routes/shepherd/rpc.js
  48. 6
      version
  49. 2
      version_build

2
LICENSE

@ -1,6 +1,6 @@
MIT License
Copyright (c) 2017 SuperNET
Copyright (c) 2017 - 2018 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

2
gui/EasyDEX-GUI

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

310
main.js

@ -17,7 +17,6 @@ const express = require('express');
const bodyParser = require('body-parser');
const fsnode = require('fs');
const fs = require('fs-extra');
const numCPUs = require('os').cpus().length;
const Promise = require('bluebird');
const arch = require('arch');
@ -56,8 +55,8 @@ app.setVersion(appBasicInfo.version);
shepherd.createAgamaDirs();
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('sys info:');
shepherd.writeLog(`totalmem_readable: ${formatBytes(os.totalmem())}`);
@ -68,7 +67,9 @@ shepherd.writeLog(`platform: ${osPlatform}`);
shepherd.writeLog(`os_release: ${os.release()}`);
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('sys info:');
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.setConfKMD();
shepherd.setConfKMD('CHIPS');
// shepherd.setConfKMD('CHIPS');
guiapp.use((req, res, next) => {
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`,
copyright: 'Released under the MIT license',
credits: 'SuperNET Team',
})
}
if (osPlatform === 'linux') {
});
} else if (osPlatform === 'linux') {
process.setFdLimit(appConfig.maxDescriptors.linux);
}
});
@ -149,9 +149,7 @@ const io = require('socket.io').listen(server);
const _zcashParamsExist = shepherd.zcashParamsExist();
let willQuitApp = false;
let mainWindow;
let loadingWindow;
let appCloseWindow;
let appSettingsWindow;
let closeAppAfterLoading = false;
let forceQuitApp = false;
@ -165,123 +163,13 @@ if (os.platform() === 'win32') {
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
function forseCloseApp() {
forceQuitApp = true;
app.quit();
}
function setDefaultAppSettings() {
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);
}
app.on('ready', () => createWindow('open', process.argv.indexOf('dexonly') > -1 ? true : null));
function createAppCloseWindow() {
// 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) {
if (appSettingsWindow) {
destroyAppSettingsWindow();
if (process.argv.indexOf('spvcoins=all/add-all') > -1) {
shepherd.startSPV('kmd');
}
if (status === 'open') {
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) {
mainWindow = null;
loadingWindow = null;
pm2Exit();
}
const staticMenu = Menu.buildFromTemplate([ // if static
@ -383,6 +222,35 @@ function createWindow(status, hideLoadingWindow) {
{ role: 'selectall' },
]);
// check if agama is already running
portscanner.checkPortStatus(appConfig.agamaPort, '127.0.0.1', (error, status) => {
// Status is 'open' if currently in use or 'closed' if available
if (status === 'closed') {
server.listen(appConfig.agamaPort, () => {
shepherd.log(`guiapp and sockets.io are listening on port ${appConfig.agamaPort}`);
shepherd.writeLog(`guiapp and sockets.io are listening on port ${appConfig.agamaPort}`);
// start sockets.io
io.set('origins', appConfig.dev ? 'http://127.0.0.1:3000' : `http://127.0.0.1:${appConfig.agamaPort}`); // set origin
});
// initialise window
mainWindow = new BrowserWindow({ // dirty hack to prevent main window flash on quit
width: closeAppAfterLoading ? 1 : 1280,
height: closeAppAfterLoading ? 1 : 850,
icon: agamaIcon,
show: false,
});
if (appConfig.dev) {
mainWindow.loadURL('http://127.0.0.1:3000');
} else {
mainWindow.loadURL(`http://${appConfig.host}:${appConfig.agamaPort}/gui/EasyDEX-GUI/react/build`);
}
shepherd.setIO(io); // pass sockets object to shepherd router
shepherd.setVar('appBasicInfo', appBasicInfo);
shepherd.setVar('appSessionHash', appSessionHash);
// load our index.html (i.e. easyDEX GUI)
shepherd.writeLog('show edex gui');
mainWindow.appConfig = appConfig;
@ -407,24 +275,62 @@ function createWindow(status, hideLoadingWindow) {
mainWindow.activeSection = 'wallets';
mainWindow.argv = process.argv;
mainWindow.getAssetChainPorts = shepherd.getAssetChainPorts;
if (appConfig.dev) {
mainWindow.loadURL('http://127.0.0.1:3000');
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.loadURL(`http://${appConfig.host}:${appConfig.agamaPort}/gui/EasyDEX-GUI/react/build`);
mainWindow = new BrowserWindow({
width: 500,
height: 355,
frame: false,
icon: agamaIcon,
show: false,
});
mainWindow.setResizable(false);
mainWindow.forseCloseApp = forseCloseApp;
willQuitApp = true;
server.listen(appConfig.agamaPort + 1, () => {
shepherd.log(`guiapp and sockets.io are listening on port ${appConfig.agamaPort + 1}`);
shepherd.writeLog(`guiapp and sockets.io are listening on port ${appConfig.agamaPort + 1}`);
});
mainWindow.loadURL(`http://${appConfig.host}:${appConfig.agamaPort + 1}/gui/startup/agama-instance-error.html`);
shepherd.log('another agama app is already running');
}
mainWindow.webContents.on('did-finish-load', () => {
setTimeout(() => {
mainWindow.show();
if (hideLoadingWindow &&
loadingWindow) {
loadingWindow.hide();
}
}, 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
@ -491,7 +397,9 @@ function createWindow(status, hideLoadingWindow) {
let _appClosingInterval;
// shepherd.killRogueProcess('marketmaker');
if (process.argv.indexOf('dexonly') > -1) {
shepherd.killRogueProcess('marketmaker');
}
if (!Object.keys(shepherd.coindInstanceRegistry).length ||
!appConfig.stopNativeDaemonsOnQuit) {
closeApp();
@ -506,68 +414,50 @@ function createWindow(status, hideLoadingWindow) {
}
}
// if window closed we kill iguana proc
// close app
mainWindow.on('closed', () => {
appExit();
});
});
}
}
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
// 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
/*if (process.platform !== 'darwin' || process.platform !== 'linux' || process.platform !== 'win32') {
app.quit()
}*/
})
});
// Emitted before the application starts closing its windows.
// Calling event.preventDefault() will prevent the default behaviour, which is terminating the application.
app.on('before-quit', (event) => {
shepherd.log('before-quit');
// 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();
}*/
if (process.argv.indexOf('dexonly') > -1) {
shepherd.killRogueProcess('marketmaker');
}
});
// 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.
app.on('will-quit', (event) => {
if (!forceQuitApp &&
mainWindow === null &&
loadingWindow != null) {
if (!forceQuitApp) {
// loading window is still open
shepherd.log('will-quit while loading window active');
event.preventDefault();
// event.preventDefault();
}
});
// Emitted when the application is quitting.
// Calling event.preventDefault() will prevent the default behaviour, which is terminating the application.
app.on('quit', (event) => {
if (!forceQuitApp &&
mainWindow === null &&
loadingWindow != null) {
if (!forceQuitApp) {
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

22
package.json

@ -1,7 +1,7 @@
{
"name": "agama-app",
"productName": "Agama",
"version": "0.2.24",
"version": "0.2.29",
"description": "Agama Wallet Desktop App",
"main": "main.js",
"scripts": {
@ -10,32 +10,38 @@
"make-rpm": "node make-rpm.js",
"make-deb": "node make-deb.js"
},
"repository": "https://github.com/SuperNETorg/Agama/",
"repository": "https://github.com/KomodoPlatform/Agama/",
"homepage": "http://supernet.org",
"keywords": [
"agama",
"SuperNET",
"komodo",
"pax"
"multicoin",
"wallet",
"spv"
],
"author": "SuperNET",
"author": "SuperNET Team",
"license": "MIT",
"devDependencies": {
"electron": "1.6.5",
"electron": "1.7.10",
"electron-installer-debian": "^0.6.0",
"electron-installer-redhat": "^0.5.0"
},
"dependencies": {
"adm-zip": "^0.4.7",
"arch": "^2.1.0",
"async": "^2.6.0",
"bigi": "^1.4.2",
"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",
"body-parser": "^1.15.2",
"buffer-reverse": "^1.0.1",
"coinkey": "^2.0.0",
"coinselect": "github:bitcoinjs/coinselect",
"electron": "1.6.5",
"electron": "1.8.2",
"express": "^4.14.0",
"fix-path": "^2.1.0",
"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',
label: 'Support',
label: 'Debug',
submenu: [
{
label: 'Supernet.org',
click () {
if (process.platform === 'linux') {
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: 'Reset settings',
click (item, focusedWindow) {
focusedWindow.resetSettings();
}
},
{
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') {

24
routes/appConfig.js

@ -17,6 +17,9 @@ const appConfig = {
cliStopTimeout: 1000,
failedRPCAttemptsThreshold: 10,
stopNativeDaemonsOnQuit: true,
lang: 'EN',
rpc2cli: false,
fiatRates: false,
},
schema: {
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',
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 = {
/*zcash: {
address: '173.212.225.176',
port: 50032,
proto: 'tcp',
},*/
coqui: { // !estimatefee
address: 'electrum1.cipig.net',
port: 10011,
@ -141,6 +136,61 @@ let electrumServers = {
'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
address: 'electrum1.cipig.net',
port: 10004,
@ -169,13 +219,21 @@ let electrumServers = {
proto: 'tcp',
txfee: 100000000,
abbr: 'DOGE',
serverList: [
'173.212.225.176:50015',
'136.243.45.140:50015'
],
},
viacoin: { // !estimatefee
address: 'vialectrum.bitops.me',
port: 50002,
proto: 'ssl',
address: '173.212.225.176',
port: 50033,
proto: 'tcp',
txfee: 100000,
abbr: 'VIA',
serverList: [
'173.212.225.176:50033',
'136.243.45.140:50033'
],
},
vertcoin: {
address: '173.212.225.176',
@ -183,6 +241,10 @@ let electrumServers = {
proto: 'tcp',
txfee: 100000,
abbr: 'VTC',
serverList: [
'173.212.225.176:50088',
'136.243.45.140:50088'
],
},
namecoin: {
address: '173.212.225.176',
@ -190,6 +252,10 @@ let electrumServers = {
proto: 'tcp',
txfee: 100000,
abbr: 'NMC',
serverList: [
'173.212.225.176:50036',
'136.243.45.140:50036'
],
},
monacoin: { // !estimatefee
address: '173.212.225.176',
@ -197,13 +263,21 @@ let electrumServers = {
proto: 'tcp',
txfee: 100000,
abbr: 'MONA',
serverList: [
'173.212.225.176:50002',
'136.243.45.140:50002'
],
},
litecoin: {
address: '173.212.225.176',
port: 50012,
proto: 'tcp',
txfee: 10000,
txfee: 100000,
abbr: 'LTC',
serverList: [
'173.212.225.176:50012',
'136.243.45.140:50012'
],
},
faircoin: {
address: '173.212.225.176',
@ -211,13 +285,21 @@ let electrumServers = {
proto: 'tcp',
txfee: 1000000,
abbr: 'FAIR',
serverList: [
'173.212.225.176:50005',
'136.243.45.140:50005'
],
},
digibyte: {
dgb: {
address: '173.212.225.176',
port: 50022,
proto: 'tcp',
txfee: 100000,
abbr: 'DGB',
serverList: [
'173.212.225.176:50022',
'136.243.45.140:50022'
],
},
dash: {
address: '173.212.225.176',
@ -225,6 +307,10 @@ let electrumServers = {
proto: 'tcp',
txfee: 10000,
abbr: 'DASH',
serverList: [
'173.212.225.176:50098',
'136.243.45.140:50098'
],
},
crown: {
address: '173.212.225.176',
@ -232,12 +318,70 @@ let electrumServers = {
proto: 'tcp',
txfee: 10000,
abbr: 'CRW',
serverList: [
'173.212.225.176:50041',
'136.243.45.140:50041'
],
},
bitcoin: {
address: '173.212.225.176',
btc: {
address: 'e-x.not.fyi',
port: 50001,
proto: 'tcp',
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
address: '173.212.225.176',
@ -245,18 +389,131 @@ let electrumServers = {
proto: 'tcp',
txfee: 50000,
abbr: 'ARG',
serverList: [
'173.212.225.176:50081',
'136.243.45.140:50081'
],
},
chips: { // !estimatefee
address: '173.212.225.176',
port: 50076,
address: 'electrum1.cipig.net',
port: 10053,
proto: 'tcp',
txfee: 10000,
abbr: 'CHIPS',
serverList: [
'173.212.225.176:50076',
'136.243.45.140:50076'
'electrum1.cipig.net:10053',
'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;

173
routes/electrumjs/electrumjs.networks.js

@ -2,8 +2,8 @@
var bitcoin = require('bitcoinjs-lib');
var networks = exports;
Object.keys(bitcoin.networks).forEach(function(key){
networks[key] = bitcoin.networks[key]
Object.keys(bitcoin.networks).forEach((key) => {
networks[key] = bitcoin.networks[key];
});
networks.litecoin = {
@ -15,8 +15,8 @@ networks.litecoin = {
pubKeyHash: 0x30,
scriptHash: 0x32,
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 = {
messagePrefix: '\x19Dogecoin Signed Message:\n',
@ -27,7 +27,7 @@ networks.dogecoin = {
pubKeyHash: 0x1e,
scriptHash: 0x16,
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
@ -192,8 +192,81 @@ networks.chips = {
dustThreshold: 1000,
};
/*networks.zcash = {
networks.btg = {
messagePrefix: '\x19BitcoinGold Signed Message:\n',
bip32: {
public: 0x0488b21e,
private: 0x0488ade4,
},
pubKeyHash: 0x26,
scriptHash: 0x17,
wif: 0x80,
dustThreshold: 1000,
};
networks.bch = {
messagePrefix: '\x19BitcoinCash Signed Message:\n',
bip32: {
public: 0x0488b21e,
private: 0x0488ade4,
},
pubKeyHash: 0x0,
scriptHash: 0x5,
wif: 0x80,
dustThreshold: 1000,
};
networks.blk = {
messagePrefix: '\x19BlackCoin Signed Message:\n',
bip32: {
public: 0x0488b21e,
private: 0x0488ade4,
},
pubKeyHash: 0x19,
scriptHash: 0x55,
wif: 0x99,
dustThreshold: 1000,
isPoS: true,
};
networks.sib = {
messagePrefix: '\x19SibCoin Signed Message:\n',
bip32: {
public: 0x0488b21e,
private: 0x0488ade4,
},
pubKeyHash: 0x3f,
scriptHash: 0x28,
wif: 0x80,
dustThreshold: 1000,
};
networks.zcash = {
messagePrefix: '\x19Zcash Signed Message:\n',
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,
@ -202,4 +275,90 @@ networks.chips = {
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 = {
'komodod': '7771',
'markermaker': '7783',
'PIZZA': '11608',
'BEER': '8923',
'CHIPS': '57776',
'SUPERNET': '11341',
'REVS': '10196',
@ -20,38 +24,10 @@ const assetChainPorts = {
'KV': '8299',
'CEAL': '11116',
'MESH': '9455',
'USD': '13967',
'CHF': '15312',
'CAD': '8720',
'BRL': '9914',
'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'
'AXO': '12927',
'ETOMIC': '10271',
'VOTE': '8012',
'BTCH': '8800',
};
module.exports = assetChainPorts;

12
routes/shepherd.js

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

150
routes/shepherd/addCoinShortcuts.js

@ -1,13 +1,21 @@
const electrumServers = require('../electrumjs/electrumServers');
module.exports = (shepherd) => {
shepherd.startSPV = (coin) => {
if (coin === 'KMD+REVS+JUMBLR') {
shepherd.addElectrumCoin('KMD');
shepherd.addElectrumCoin('REVS');
shepherd.addElectrumCoin('JUMBLR');
} else {
if (process.argv.indexOf('spvcoins=all/add-all') > -1) {
for (let key in electrumServers) {
shepherd.addElectrumCoin(electrumServers[key].abbr);
}
} else {
shepherd.addElectrumCoin(coin);
}
}
}
shepherd.startKMDNative = (selection, isManual) => {
if (isManual) {
@ -32,7 +40,144 @@ module.exports = (shepherd) => {
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 === '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) => {
@ -82,7 +227,8 @@ module.exports = (shepherd) => {
body: JSON.stringify({
herd: 'komodod',
options: herdData[i],
})
token: shepherd.appSessionHash,
}),
};
shepherd.request(options, (error, response, body) => {

19
routes/shepherd/appInfo.js

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

18
routes/shepherd/auth.js

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

239
routes/shepherd/binsTestUtil.js

@ -1,239 +0,0 @@
module.exports = (shepherd) => {
shepherd.testClearAll = () => {
return new shepherd.Promise((resolve, reject) => {
shepherd.fs.removeSync(`${iguanaTestDir}`);
resolve('done');
});
}
shepherd.testBins = (daemonName) => {
return new shepherd.Promise((resolve, reject) => {
const _bins = {
komodod: shepherd.komododBin,
komodoCli: shepherd.komodocliBin,
};
const _arg = null;
let _pid;
shepherd.log('testBins exec ' + _bins[daemonName]);
if (!shepherd.fs.existsSync(shepherd.agamaTestDir)) {
shepherd.fs.mkdirSync(shepherd.agamaTestDir);
}
try {
shepherd._fs.access(`${shepherd.agamaTestDir}/${daemonName}Test.log`, shepherd.fs.constants.R_OK, (err) => {
if (!err) {
try {
shepherd._fs.unlinkSync(`${shepherd.agamaTestDir}/${daemonName}Test.log`);
} catch (e) {}
} else {
shepherd.log(`path ${shepherd.agamaTestDir}/${daemonName}Test.log doesnt exist`);
}
});
} catch (e) {}
if (daemonName === 'komodod') {
try {
shepherd._fs.access(`${iguanaTestDir}/debug.log`, shepherd.fs.constants.R_OK, (err) => {
if (!err) {
shepherd._fs.unlinkSync(`${iguanaTestDir}/db.log`);
shepherd._fs.unlinkSync(`${iguanaTestDir}/debug.log`);
shepherd._fs.unlinkSync(`${iguanaTestDir}/komodo.conf`);
shepherd._fs.unlinkSync(`${iguanaTestDir}/komodod.pid`);
shepherd._fs.unlinkSync(`${iguanaTestDir}/komodostate`);
shepherd._fs.unlinkSync(`${iguanaTestDir}/realtime`);
shepherd._fs.unlinkSync(`${iguanaTestDir}/wallet.dat`);
shepherd._fs.unlinkSync(`${iguanaTestDir}/.lock`);
shepherd.fs.removeSync(`${iguanaTestDir}/blocks`);
shepherd.fs.removeSync(`${iguanaTestDir}/chainstate`);
shepherd.fs.removeSync(`${iguanaTestDir}/database`);
execKomodod();
} else {
shepherd.log(`test: nothing to remove in ${iguanaTestDir}`);
execKomodod();
}
});
} catch (e) {}
const execKomodod = () => {
let _komododTest = {
port: 'unknown',
start: 'unknown',
getinfo: 'unknown',
errors: {
assertFailed: false,
zcashParams: false,
},
};
const _komodoConf = 'rpcuser=user83f3afba8d714993\n' +
'rpcpassword=0d4430ca1543833e35bce5a0cc9e16b3\n' +
'server=1\n' +
'addnode=78.47.196.146\n' +
'addnode=5.9.102.210\n' +
'addnode=178.63.69.164\n' +
'addnode=88.198.65.74\n' +
'addnode=5.9.122.241\n' +
'addnode=144.76.94.3\n' +
'addnode=144.76.94.38\n' +
'addnode=89.248.166.91\n' +
'addnode=148.251.57.148\n' +
'addnode=149.56.28.84\n' +
'addnode=176.9.26.39\n' +
'addnode=94.102.63.199\n' +
'addnode=94.102.63.200\n' +
'addnode=104.255.64.3\n' +
'addnode=221.121.144.140\n' +
'addnode=103.18.58.150\n' +
'addnode=103.18.58.146\n' +
'addnode=213.202.253.10\n' +
'addnode=185.106.121.32\n' +
'addnode=27.100.36.201\n';
shepherd.fs.writeFile(`${iguanaTestDir}/komodo.conf`, _komodoConf, (err) => {
if (err) {
shepherd.log(`test: error writing komodo conf in ${iguanaTestDir}`);
}
});
shepherd.portscanner.checkPortStatus('7771', '127.0.0.1', (error, status) => {
// Status is 'open' if currently in use or 'closed' if available
if (status === 'closed') {
_komododTest.port = 'passed';
} else {
_komododTest.port = 'failed';
}
});
/*pm2.connect(true,function(err) { //start up pm2 god
if (err) {
shepherd.error(err);
process.exit(2);
}
pm2.start({
script: shepherd.komododBin, // path to binary
name: 'komodod',
exec_mode : 'fork',
args: [
'-daemon=0',
'-addnode=78.47.196.146',
`-datadir=${iguanaTestDir}/`
],
output: `${iguanaTestDir}/komododTest.log`,
mergeLogs: true,
}, function(err, apps) {
if (apps[0] &&
apps[0].process &&
apps[0].process.pid) {
_komododTest.start = 'success';
shepherd.log(`test: got komodod instance pid = ${apps[0].process.pid}`);
shepherd.writeLog(`test: komodod started with pid ${apps[0].process.pid}`);
} else {
_komododTest.start = 'failed';
shepherd.log(`unable to start komodod`);
}
pm2.disconnect(); // Disconnect from PM2
if (err) {
shepherd.writeLog(`test: error starting komodod`);
shepherd.log(`komodod fork err: ${err}`);
// throw err;
}
});
});*/
setTimeout(() => {
const options = {
url: `http://localhost:7771`,
method: 'POST',
auth: {
user: 'user83f3afba8d714993',
pass: '0d4430ca1543833e35bce5a0cc9e16b3',
},
body: JSON.stringify({
agent: 'bitcoinrpc',
method: 'getinfo',
}),
};
shepherd.request(options, (error, response, body) => {
if (response &&
response.statusCode &&
response.statusCode === 200) {
// res.end(body);
shepherd.log(JSON.stringify(body, null, '\t'));
} else {
// res.end(body);
shepherd.log(JSON.stringify(body, null, '\t'));
}
});
}, 10000);
setTimeout(() => {
pm2.delete('komodod');
resolve(_komododTest);
}, 20000);
}
// komodod debug.log hooks
//"{\"result\":{\"version\":1000850,\"protocolversion\":170002,\"KMDversion\":\"0.1.1\",\"notarized\":0,\"notarizedhash\":\"0000000000000000000000000000000000000000000000000000000000000000\",\"notarizedtxid\":\"0000000000000000000000000000000000000000000000000000000000000000\",\"notarizedtxid_height\":\"mempool\",\"notarized_confirms\":0,\"walletversion\":60000,\"balance\":0.00000000,\"interest\":0.00000000,\"blocks\":128,\"longestchain\":472331,\"timeoffset\":0,\"tiptime\":1473827710,\"connections\":1,\"proxy\":\"\",\"difficulty\":1,\"testnet\":false,\"keypoololdest\":1504118047,\"keypoolsize\":101,\"paytxfee\":0.00000000,\"relayfee\":0.00000100,\"errors\":\"\"},\"error\":null,\"id\":null}\n"
//2017-08-30 17:51:33 Error: Cannot find the Zcash network parameters in the following directory:
//"/home/pbca/.zcash-params"
//Please run 'zcash-fetch-params' or './zcutil/fetch-params.sh' and then restart.
//EXCEPTION: St13runtime_error
//Assertion failed.
//2017-08-30 17:51:14 Using config file /home/pbca/.iguana/test/komodo.conf
//2017-08-30 18:23:43 UpdateTip: new best=0a47c1323f393650f7221c217d19d149d002d35444f47fde61be2dd90fbde8e6 height=1 log2_work=5.0874628 tx=2 date=2016-09-13 19:04:01 progress=0.000001 cache=0.0MiB(1tx)
//2017-08-30 18:23:43 UpdateTip: new best=05076a4e1fc9af0f5fda690257b17ae20c12d4796dfba1624804d012c9ec00be height=2 log2_work=5.6724253 tx=3 date=2016-09-13 19:05:28 progress=0.000001 cache=0.0MiB(2tx)
/*shepherd.execFile(`${shepherd.komododBin}`, _arg, {
maxBuffer: 1024 * 10000 // 10 mb
}, function(error, stdout, stderr) {
shepherd.writeLog(`stdout: ${stdout}`);
shepherd.writeLog(`stderr: ${stderr}`);
if (error !== null) {
console.log(`exec error: ${error}`);
shepherd.writeLog(`exec error: ${error}`);
if (error.toString().indexOf('using -reindex') > -1) {
shepherd.io.emit('service', {
komodod: {
error: 'run -reindex',
}
});
}
}
});*/
}
});
}
// komodod datadir location test
shepherd.testLocation = (path) => {
return new shepherd.Promise((resolve, reject) => {
if (path.indexOf(' ') > -1) {
shepherd.log(`error testing path ${path}`);
resolve(-1);
} else {
shepherd.fs.lstat(path, (err, stats) => {
if (err) {
shepherd.log(`error testing path ${path}`);
resolve(-1);
} else {
if (stats.isDirectory()) {
resolve(true);
} else {
shepherd.log(`error testing path ${path} not a folder`);
resolve(false);
}
}
});
}
});
}
return shepherd;
};

9
routes/shepherd/coindWalletKeys.js

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

9
routes/shepherd/coins.js

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

18
routes/shepherd/coinsList.js

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

27
routes/shepherd/config.js

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

136
routes/shepherd/daemonControl.js

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

20
routes/shepherd/dashboardUpdate.js

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

18
routes/shepherd/debugLog.js

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

18
routes/shepherd/downloadBins.js

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

29
routes/shepherd/downloadPatch.js

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

9
routes/shepherd/downloadZcparams.js

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

372
routes/shepherd/elections.js

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

67
routes/shepherd/electrum/auth.js

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

23
routes/shepherd/electrum/balance.js

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

18
routes/shepherd/electrum/block.js

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

47
routes/shepherd/electrum/coins.js

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

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

9
routes/shepherd/electrum/estimate.js

@ -1,5 +1,6 @@
module.exports = (shepherd) => {
shepherd.get('/electrum/estimatefee', (req, res, next) => {
if (shepherd.checkToken(req.query.token)) {
const ecl = new shepherd.electrumJSCore(shepherd.electrumServers[req.query.network].port, shepherd.electrumServers[req.query.network].address, shepherd.electrumServers[req.query.network].proto); // tcp or tls
ecl.connect();
@ -15,6 +16,14 @@ module.exports = (shepherd) => {
res.end(JSON.stringify(successObj));
});
} else {
const errorObj = {
msg: 'error',
result: 'unauthorized access',
};
res.end(JSON.stringify(errorObj));
}
});
shepherd.estimateTxSize = (numVins, numOuts) => {

2
routes/shepherd/electrum/interest.js

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

267
routes/shepherd/electrum/keys.js

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

27
routes/shepherd/electrum/listunspent.js

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

30
routes/shepherd/electrum/merkle.js

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

81
routes/shepherd/electrum/network.js

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

279
routes/shepherd/electrum/transactions.js

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

9
routes/shepherd/kickstart.js

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

27
routes/shepherd/log.js

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

35
routes/shepherd/pin.js

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

18
routes/shepherd/quitDaemon.js

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

121
routes/shepherd/rpc.js

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

6
version

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

2
version_build

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